256 lines
8.1 KiB
C
256 lines
8.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Phytium Pe220x display controller DRM driver
|
|
*
|
|
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
|
|
*/
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_atomic.h>
|
|
#include <asm/neon.h>
|
|
#include <linux/delay.h>
|
|
#include "phytium_display_drv.h"
|
|
#include "pe220x_reg.h"
|
|
#include "phytium_crtc.h"
|
|
#include "phytium_plane.h"
|
|
#include "phytium_fb.h"
|
|
#include "phytium_gem.h"
|
|
|
|
void pe220x_dc_hw_disable(struct drm_crtc *crtc);
|
|
|
|
static const unsigned int pe220x_primary_formats[] = {
|
|
DRM_FORMAT_ARGB2101010,
|
|
DRM_FORMAT_ABGR2101010,
|
|
DRM_FORMAT_RGBA1010102,
|
|
DRM_FORMAT_BGRA1010102,
|
|
DRM_FORMAT_ARGB8888,
|
|
DRM_FORMAT_ABGR8888,
|
|
DRM_FORMAT_RGBA8888,
|
|
DRM_FORMAT_BGRA8888,
|
|
DRM_FORMAT_XRGB8888,
|
|
DRM_FORMAT_XBGR8888,
|
|
DRM_FORMAT_RGBX8888,
|
|
DRM_FORMAT_BGRX8888,
|
|
DRM_FORMAT_ARGB4444,
|
|
DRM_FORMAT_ABGR4444,
|
|
DRM_FORMAT_RGBA4444,
|
|
DRM_FORMAT_BGRA4444,
|
|
DRM_FORMAT_XRGB4444,
|
|
DRM_FORMAT_XBGR4444,
|
|
DRM_FORMAT_RGBX4444,
|
|
DRM_FORMAT_BGRX4444,
|
|
DRM_FORMAT_ARGB1555,
|
|
DRM_FORMAT_ABGR1555,
|
|
DRM_FORMAT_RGBA5551,
|
|
DRM_FORMAT_BGRA5551,
|
|
DRM_FORMAT_XRGB1555,
|
|
DRM_FORMAT_XBGR1555,
|
|
DRM_FORMAT_RGBX5551,
|
|
DRM_FORMAT_BGRX5551,
|
|
DRM_FORMAT_RGB565,
|
|
DRM_FORMAT_BGR565,
|
|
DRM_FORMAT_YUYV,
|
|
DRM_FORMAT_UYVY,
|
|
DRM_FORMAT_NV16,
|
|
DRM_FORMAT_NV12,
|
|
DRM_FORMAT_NV21,
|
|
};
|
|
|
|
static uint64_t pe220x_primary_formats_modifiers[] = {
|
|
DRM_FORMAT_MOD_LINEAR,
|
|
DRM_FORMAT_MOD_INVALID
|
|
};
|
|
|
|
static uint64_t pe220x_cursor_formats_modifiers[] = {
|
|
DRM_FORMAT_MOD_LINEAR,
|
|
DRM_FORMAT_MOD_INVALID
|
|
};
|
|
|
|
static const unsigned int pe220x_cursor_formats[] = {
|
|
DRM_FORMAT_ARGB8888,
|
|
};
|
|
|
|
void pe220x_dc_hw_vram_init(struct phytium_display_private *priv, resource_size_t vram_addr,
|
|
resource_size_t vram_size)
|
|
{
|
|
uint32_t config;
|
|
uint32_t group_offset = priv->address_transform_base;
|
|
|
|
phytium_writel_reg(priv, (vram_addr & SRC_ADDR_MASK) >> SRC_ADDR_OFFSET,
|
|
group_offset, PE220X_DC_ADDRESS_TRANSFORM_SRC_ADDR);
|
|
phytium_writel_reg(priv, (vram_size >> SIZE_OFFSET) | ADDRESS_TRANSFORM_ENABLE,
|
|
group_offset, PE220X_DC_ADDRESS_TRANSFORM_SIZE);
|
|
config = phytium_readl_reg(priv, group_offset, PE220X_DC_ADDRESS_TRANSFORM_DST_ADDR);
|
|
phytium_writel_reg(priv, config, group_offset, PE220X_DC_ADDRESS_TRANSFORM_DST_ADDR);
|
|
}
|
|
|
|
void pe220x_dc_hw_config_pix_clock(struct drm_crtc *crtc, int clock)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct phytium_display_private *priv = dev->dev_private;
|
|
struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc);
|
|
int phys_pipe = phytium_crtc->phys_pipe;
|
|
int ret = 0;
|
|
|
|
/* config pix clock */
|
|
phytium_writel_reg(priv, FLAG_REQUEST | CMD_PIXEL_CLOCK | (clock & PIXEL_CLOCK_MASK),
|
|
0, PE220X_DC_CMD_REGISTER(phys_pipe));
|
|
ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(phys_pipe),
|
|
FLAG_REQUEST, FLAG_REPLY);
|
|
if (ret < 0)
|
|
DRM_ERROR("%s: failed to set pixel clock\n", __func__);
|
|
}
|
|
|
|
void pe220x_dc_hw_reset(struct drm_crtc *crtc)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct phytium_display_private *priv = dev->dev_private;
|
|
struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc);
|
|
int config = 0;
|
|
int phys_pipe = phytium_crtc->phys_pipe;
|
|
|
|
/* disable pixel clock for bmc mode */
|
|
if (phys_pipe == 0)
|
|
pe220x_dc_hw_disable(crtc);
|
|
|
|
config = phytium_readl_reg(priv, 0, PE220X_DC_CLOCK_CONTROL);
|
|
config &= (~(DC0_CORE_RESET | DC1_CORE_RESET | AXI_RESET | AHB_RESET));
|
|
|
|
if (phys_pipe == 0) {
|
|
phytium_writel_reg(priv, config | DC0_CORE_RESET,
|
|
0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET,
|
|
0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET | AHB_RESET,
|
|
0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET,
|
|
0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config | DC0_CORE_RESET,
|
|
0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config, 0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
} else {
|
|
phytium_writel_reg(priv, config | DC1_CORE_RESET,
|
|
0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET,
|
|
0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET | AHB_RESET,
|
|
0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET,
|
|
0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config | DC1_CORE_RESET,
|
|
0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config, 0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
}
|
|
}
|
|
|
|
void pe220x_dc_hw_disable(struct drm_crtc *crtc)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct phytium_display_private *priv = dev->dev_private;
|
|
struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc);
|
|
int config = 0;
|
|
int phys_pipe = phytium_crtc->phys_pipe;
|
|
|
|
/* clear framebuffer */
|
|
phytium_writel_reg(priv, CLEAR_VALUE_BLACK, priv->dc_reg_base[phys_pipe],
|
|
PHYTIUM_DC_FRAMEBUFFER_CLEARVALUE);
|
|
config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe],
|
|
PHYTIUM_DC_FRAMEBUFFER_CONFIG);
|
|
config |= FRAMEBUFFER_CLEAR;
|
|
phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe],
|
|
PHYTIUM_DC_FRAMEBUFFER_CONFIG);
|
|
|
|
/* disable cursor */
|
|
config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], PHYTIUM_DC_CURSOR_CONFIG);
|
|
config = ((config & (~CURSOR_FORMAT_MASK)) | CURSOR_FORMAT_DISABLED);
|
|
phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe], PHYTIUM_DC_CURSOR_CONFIG);
|
|
mdelay(20);
|
|
|
|
/* reset pix clock */
|
|
pe220x_dc_hw_config_pix_clock(crtc, 0);
|
|
|
|
if (phys_pipe == 0) {
|
|
config = phytium_readl_reg(priv, 0, PE220X_DC_CLOCK_CONTROL);
|
|
phytium_writel_reg(priv, config | DC0_CORE_RESET, 0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config & (~DC0_CORE_RESET), 0, PE220X_DC_CLOCK_CONTROL);
|
|
} else {
|
|
config = phytium_readl_reg(priv, 0, PE220X_DC_CLOCK_CONTROL);
|
|
phytium_writel_reg(priv, config | DC1_CORE_RESET, 0, PE220X_DC_CLOCK_CONTROL);
|
|
udelay(20);
|
|
phytium_writel_reg(priv, config & (~DC1_CORE_RESET), 0, PE220X_DC_CLOCK_CONTROL);
|
|
}
|
|
udelay(20);
|
|
}
|
|
|
|
int pe220x_dc_hw_fb_format_check(const struct drm_mode_fb_cmd2 *mode_cmd, int count)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (mode_cmd->modifier[count] != DRM_FORMAT_MOD_LINEAR) {
|
|
DRM_ERROR("unsupported fb modifier 0x%llx\n", mode_cmd->modifier[count]);
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void pe220x_dc_hw_plane_get_primary_format(const uint64_t **format_modifiers,
|
|
const uint32_t **formats,
|
|
uint32_t *format_count)
|
|
{
|
|
*format_modifiers = pe220x_primary_formats_modifiers;
|
|
*formats = pe220x_primary_formats;
|
|
*format_count = ARRAY_SIZE(pe220x_primary_formats);
|
|
}
|
|
|
|
void pe220x_dc_hw_plane_get_cursor_format(const uint64_t **format_modifiers,
|
|
const uint32_t **formats,
|
|
uint32_t *format_count)
|
|
{
|
|
*format_modifiers = pe220x_cursor_formats_modifiers;
|
|
*formats = pe220x_cursor_formats;
|
|
*format_count = ARRAY_SIZE(pe220x_cursor_formats);
|
|
}
|
|
|
|
void pe220x_dc_hw_update_primary_hi_addr(struct drm_plane *plane)
|
|
{
|
|
struct drm_device *dev = plane->dev;
|
|
struct phytium_display_private *priv = dev->dev_private;
|
|
struct phytium_plane *phytium_plane = to_phytium_plane(plane);
|
|
int phys_pipe = phytium_plane->phys_pipe;
|
|
|
|
phytium_writel_reg(priv, (phytium_plane->iova[0] >> PREFIX_SHIFT) & PREFIX_MASK,
|
|
priv->dc_reg_base[phys_pipe], PE220X_DC_FRAMEBUFFER_Y_HI_ADDRESS);
|
|
|
|
phytium_writel_reg(priv, (phytium_plane->iova[1] >> U_PREFIX_SHIFT) & U_PREFIX_MASK,
|
|
priv->dc_reg_base[phys_pipe], PE220X_DC_FRAMEBUFFER_U_HI_ADDRESS);
|
|
|
|
phytium_writel_reg(priv, (phytium_plane->iova[2] >> V_PREFIX_SHIFT) & V_PREFIX_MASK,
|
|
priv->dc_reg_base[phys_pipe], PE220X_DC_FRAMEBUFFER_V_HI_ADDRESS);
|
|
}
|
|
|
|
void pe220x_dc_hw_update_cursor_hi_addr(struct drm_plane *plane, uint64_t iova)
|
|
{
|
|
struct drm_device *dev = plane->dev;
|
|
struct phytium_display_private *priv = dev->dev_private;
|
|
struct phytium_plane *phytium_plane = to_phytium_plane(plane);
|
|
int phys_pipe = phytium_plane->phys_pipe;
|
|
int config;
|
|
|
|
config = ((iova >> CURSOR_PREFIX_SHIFT) & CURSOR_PREFIX_MASK);
|
|
phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe], PE220X_DC_CURSOR_HI_ADDRESS);
|
|
}
|