DRM phytium Add Phytium Display Engine support

Phytium Display Engine support,DC/DP driver patch.
Signed-off-by: Yang Xun <yangxun@phytium.com.cn>
Signed-off-by: Chen Baozi <chenbaozi@phytium.com.cn>
Reviewed-by:Hongbo Mao<maohongbo@phytium.com.cn>
(cherry picked from commit efc4d36af4)
Signed-off-by: Alex Shi <alexsshi@tencent.com>
This commit is contained in:
lishuo 2023-06-01 15:40:45 +08:00 committed by xuyan
parent 823ace9213
commit 9debb2ab33
37 changed files with 10487 additions and 0 deletions

View File

@ -360,6 +360,8 @@ source "drivers/gpu/drm/aspeed/Kconfig"
source "drivers/gpu/drm/mcde/Kconfig"
source "drivers/gpu/drm/phytium/Kconfig"
# Keep legacy drivers last
menuconfig DRM_LEGACY

View File

@ -120,3 +120,4 @@ obj-$(CONFIG_DRM_LIMA) += lima/
obj-$(CONFIG_DRM_PANFROST) += panfrost/
obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
obj-$(CONFIG_DRM_MCDE) += mcde/
obj-$(CONFIG_DRM_PHYTIUM) += phytium/

View File

@ -0,0 +1,7 @@
config DRM_PHYTIUM
tristate "DRM Support for Phytium Graphics Card"
depends on DRM
select DRM_KMS_HELPER
help
Choose this option if you have a phytium graphics card.
This driver provides kernel mode setting and buffer management to userspace.

View File

@ -0,0 +1,18 @@
phytium-dc-drm-y := phytium_display_drv.o \
phytium_plane.o \
phytium_crtc.o \
phytium_dp.o \
phytium_fb.o \
phytium_gem.o \
phytium_fbdev.o \
phytium_debugfs.o \
px210_dp.o \
phytium_panel.o \
px210_dc.o \
phytium_pci.o \
pe220x_dp.o \
pe220x_dc.o \
phytium_platform.o
obj-$(CONFIG_DRM_PHYTIUM) += phytium-dc-drm.o
CFLAGS_REMOVE_phytium_crtc.o += -mgeneral-regs-only

View File

@ -0,0 +1,255 @@
// 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);
}

View File

@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Phytium Pe220x display controller DRM driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PE220X_DC_H__
#define __PE220X_DC_H__
#define PE220X_DC_PIX_CLOCK_MAX (594000)
#define PE220X_DC_HDISPLAY_MAX 3840
#define PE220X_DC_VDISPLAY_MAX 2160
#define PE220X_DC_ADDRESS_MASK 0x7f
extern void pe220x_dc_hw_vram_init(struct phytium_display_private *priv,
resource_size_t vram_addr,
resource_size_t vram_size);
extern void pe220x_dc_hw_config_pix_clock(struct drm_crtc *crtc, int clock);
extern void pe220x_dc_hw_disable(struct drm_crtc *crtc);
extern int pe220x_dc_hw_fb_format_check(const struct drm_mode_fb_cmd2 *mode_cmd, int count);
extern void pe220x_dc_hw_plane_get_primary_format(const uint64_t **format_modifiers,
const uint32_t **formats,
uint32_t *format_count);
extern void pe220x_dc_hw_plane_get_cursor_format(const uint64_t **format_modifiers,
const uint32_t **formats,
uint32_t *format_count);
extern void pe220x_dc_hw_update_primary_hi_addr(struct drm_plane *plane);
extern void pe220x_dc_hw_update_cursor_hi_addr(struct drm_plane *plane, uint64_t iova);
void pe220x_dc_hw_reset(struct drm_crtc *crtc);
#endif /* __PE220X_DC_H__ */

View File

@ -0,0 +1,514 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Phytium display port DRM driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include "phytium_display_drv.h"
#include "pe220x_reg.h"
#include "phytium_dp.h"
#include "pe220x_dp.h"
static uint8_t pe220x_dp_source_lane_count[2] = {1, 1};
/* [reg][ling_rate 1.62->8.1] */
static int vco_val[12][4] = {
{0x0509, 0x0509, 0x0509, 0x0509}, /* CP_PADJ */
{0x0f00, 0x0f00, 0x0f00, 0x0f00}, /* CP_IADJ */
{0x0F08, 0x0F08, 0x0F08, 0x0F08}, /* FILT_PADJ */
{0x0061, 0x006C, 0x006C, 0x0051}, /* INTDIV */
{0x3333, 0x0000, 0x0000, 0x0000}, /* FRACDIVL */
{0x0000, 0x0000, 0x0000, 0x0000}, /* FRACDIVH */
{0x0042, 0x0048, 0x0048, 0x0036}, /* HIGH_THR */
{0x0002, 0x0002, 0x0002, 0x0002}, /* PDIAG_CTRL */
{0x0c5e, 0x0c5e, 0x0c5e, 0x0c5e}, /* VCOCAL_PLLCNT_START */
{0x00c7, 0x00c7, 0x00c7, 0x00c7}, /* LOCK_PEFCNT */
{0x00c7, 0x00c7, 0x00c7, 0x00c7}, /* LOCK_PLLCNT_START */
{0x0005, 0x0005, 0x0005, 0x0005}, /* LOCK_PLLCNT_THR */
};
/* [link_rate][swing][emphasis] */
static int mgnfs_val[4][4][4] = {
/* 1.62Gbps */
{
{0x0026, 0x001f, 0x0012, 0x0000},
{0x0013, 0x0013, 0x0000, 0x0000},
{0x0006, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 2.7Gbps */
{
{0x0026, 0x001f, 0x0012, 0x0000},
{0x0013, 0x0013, 0x0000, 0x0000},
{0x0006, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 5.4Gbps */
{
{0x001f, 0x0013, 0x005, 0x0000},
{0x0018, 0x006, 0x0000, 0x0000},
{0x000c, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 8.1Gbps */
{
{0x0026, 0x0013, 0x005, 0x0000},
{0x0013, 0x006, 0x0000, 0x0000},
{0x0006, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
};
/* [link_rate][swing][emphasis] */
static int cpost_val[4][4][4] = {
/* 1.62Gbps */
{
{0x0000, 0x0014, 0x0020, 0x002a},
{0x0000, 0x0010, 0x001f, 0x0000},
{0x0000, 0x0013, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 2.7Gbps */
{
{0x0000, 0x0014, 0x0020, 0x002a},
{0x0000, 0x0010, 0x001f, 0x0000},
{0x0000, 0x0013, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 5.4Gbps */
{
{0x0005, 0x0014, 0x0022, 0x002e},
{0x0000, 0x0013, 0x0020, 0x0000},
{0x0000, 0x0013, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 8.1Gbps */
{
{0x0000, 0x0014, 0x0022, 0x002e},
{0x0000, 0x0013, 0x0020, 0x0000},
{0x0000, 0x0013, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
};
static int pe220x_dp_hw_set_phy_lane_and_rate(struct phytium_dp_device *phytium_dp,
uint8_t link_lane_count, uint32_t link_rate)
{
int port = phytium_dp->port%2;
int i = 0, data, tmp, tmp1, index = 0, mask = 0;
int timeout = 500, ret = 0;
/* set pma powerdown */
data = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++)
data |= (A3_POWERDOWN3 << (i * A3_POWERDOWN3_SHIFT));
phytium_phy_writel(phytium_dp, PE220X_PHY_PMA0_POWER(port), data);
/* lane pll disable */
data = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++) {
data |= (PLL_EN << (i * PLL_EN_SHIFT));
mask |= (((1<<PLL_EN_SHIFT) - 1) << (i * PLL_EN_SHIFT));
}
data &= ~mask;
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL_EN(port), data);
/* pma pll disable */
data = CONTROL_ENABLE & (~CONTROL_ENABLE_MASK);
phytium_phy_writel(phytium_dp, PE220X_PHY_PMA_CONTROL(port), data);
/* read pma pll disable state */
mdelay(2);
phytium_phy_readl(phytium_dp, PE220X_PHY_PMA_CONTROL2(port));
/* config link rate */
switch (link_rate) {
case 810000:
tmp = PLL_LINK_RATE_810000;
tmp1 = HSCLK_LINK_RATE_810000;
index = 3;
break;
case 540000:
tmp = PLL_LINK_RATE_540000;
tmp1 = HSCLK_LINK_RATE_540000;
index = 2;
break;
case 270000:
tmp = PLL_LINK_RATE_270000;
tmp1 = HSCLK_LINK_RATE_270000;
index = 1;
break;
case 162000:
tmp = PLL_LINK_RATE_162000;
tmp1 = HSCLK_LINK_RATE_162000;
index = 0;
break;
default:
DRM_ERROR("phytium dp rate(%d) not support\n", link_rate);
tmp = PLL_LINK_RATE_162000;
tmp1 = HSCLK_LINK_RATE_162000;
index = 0;
break;
}
/* config analog pll for link0 */
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_CLK_SEL(port), tmp);
phytium_phy_writel(phytium_dp, PE220X_PHY_HSCLK0_SEL(port), HSCLK_LINK_0);
phytium_phy_writel(phytium_dp, PE220X_PHY_HSCLK0_DIV(port), tmp1);
/* config digital pll for link0 */
phytium_phy_writel(phytium_dp, PE220X_PHY_PLLDRC0_CTRL(port), PLLDRC_LINK0);
/* common for all rate */
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_DSM_M0(port), PLL0_DSM_M0);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_VCOCAL_START(port),
PLL0_VCOCAL_START);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_VCOCAL_CTRL(port),
PLL0_VCOCAL_CTRL);
/* different for all rate */
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_CP_PADJ(port),
vco_val[0][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_CP_IADJ(port),
vco_val[1][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_CP_FILT_PADJ(port),
vco_val[2][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_INTDIV(port),
vco_val[3][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_FRACDIVL(port),
vco_val[4][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_FRACDIVH(port),
vco_val[5][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_HIGH_THR(port),
vco_val[6][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_PDIAG_CTRL(port),
vco_val[7][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_VCOCAL_PLLCNT_START(port),
vco_val[8][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_LOCK_PEFCNT(port),
vco_val[9][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_LOCK_PLLCNT_START(port),
vco_val[10][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_LOCK_PLLCNT_THR(port),
vco_val[11][index]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_PSC_A0(port),
PLL0_TX_PSC_A0);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_PSC_A2(port),
PLL0_TX_PSC_A2);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_PSC_A3(port),
PLL0_TX_PSC_A3);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_RX_PSC_A0(port),
PLL0_RX_PSC_A0);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_RX_PSC_A2(port),
PLL0_RX_PSC_A2);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_RX_PSC_A3(port),
PLL0_RX_PSC_A3);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_RX_PSC_CAL(port),
PLL0_RX_PSC_CAL);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_XCVR_CTRL(port),
PLL0_XCVR_CTRL);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_RX_GCSM1_CTRL(port),
PLL0_RX_GCSM1_CTRL);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_RX_GCSM2_CTRL(port),
PLL0_RX_GCSM2_CTRL);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_RX_PERGCSM_CTRL(port),
PLL0_RX_PERGCSM_CTRL);
/* pma pll enable */
data = CONTROL_ENABLE;
phytium_phy_writel(phytium_dp, PE220X_PHY_PMA_CONTROL(port), data);
/* lane pll enable */
data = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++)
data |= (PLL_EN << (i * PLL_EN_SHIFT));
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL_EN(port), data);
/* set pma power active */
data = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++)
data |= (A0_ACTIVE << (i * A0_ACTIVE_SHIFT));
phytium_phy_writel(phytium_dp, PE220X_PHY_PMA0_POWER(port), data);
mask = PLL0_LOCK_DONE;
do {
mdelay(1);
timeout--;
tmp = phytium_phy_readl(phytium_dp, PE220X_PHY_PMA_CONTROL2(port));
} while ((!(tmp & mask)) && timeout);
if (timeout == 0) {
DRM_ERROR("dp(%d) phy pll lock failed\n", port);
ret = -1;
}
udelay(1);
return ret;
}
static void pe220x_dp_hw_set_phy_lane_setting(struct phytium_dp_device *phytium_dp,
uint32_t link_rate, uint8_t train_set)
{
int port = phytium_dp->port % 3;
int voltage_swing = 0;
int pre_emphasis = 0, link_rate_index = 0;
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
voltage_swing = 1;
break;
case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
voltage_swing = 2;
break;
case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
voltage_swing = 3;
break;
default:
voltage_swing = 0;
break;
}
switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
case DP_TRAIN_PRE_EMPH_LEVEL_1:
pre_emphasis = 1;
break;
case DP_TRAIN_PRE_EMPH_LEVEL_2:
pre_emphasis = 2;
break;
case DP_TRAIN_PRE_EMPH_LEVEL_3:
pre_emphasis = 3;
break;
default:
pre_emphasis = 0;
break;
}
switch (link_rate) {
case 810000:
link_rate_index = 3;
break;
case 540000:
link_rate_index = 2;
break;
case 270000:
link_rate_index = 1;
break;
case 162000:
link_rate_index = 0;
break;
default:
DRM_ERROR("phytium dp rate(%d) not support\n", link_rate);
link_rate_index = 2;
break;
}
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_DIAG_ACYA(port), LOCK);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_TXCC_CTRL(port), TX_TXCC_CTRL);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_DRV(port), TX_DRV);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_MGNFS(port),
mgnfs_val[link_rate_index][voltage_swing][pre_emphasis]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_CPOST(port),
cpost_val[link_rate_index][voltage_swing][pre_emphasis]);
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_DIAG_ACYA(port), UNLOCK);
}
static int pe220x_dp_hw_init_phy(struct phytium_dp_device *phytium_dp)
{
int port = phytium_dp->port;
int i = 0, data, tmp, mask;
int timeout = 500, ret = 0;
phytium_phy_writel(phytium_dp, PE220X_PHY_APB_RESET(port), APB_RESET);
phytium_phy_writel(phytium_dp, PE220X_PHY_PIPE_RESET(port), RESET);
/* config lane to dp mode */
data = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++)
data |= (LANE_BIT << (i * LANE_BIT_SHIFT));
phytium_phy_writel(phytium_dp, PE220X_PHY_MODE(port), data);
/* pll clock enable */
data = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++)
data |= (PLL_EN << (i * PLL_EN_SHIFT));
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL_EN(port), data);
/* config input 20 bit */
data = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++)
data |= (BIT_20 << (i * BIT_20_SHIFT));
phytium_phy_writel(phytium_dp, PE220X_PHY_PMA_WIDTH(port), data);
/* config lane active power state */
data = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++)
data |= (A0_ACTIVE << (i * A0_ACTIVE_SHIFT));
phytium_phy_writel(phytium_dp, PE220X_PHY_PMA0_POWER(port), data);
/* link reset */
phytium_phy_writel(phytium_dp, PE220X_PHY_LINK_RESET(port), LINK_RESET);
phytium_phy_writel(phytium_dp, PE220X_PHY_SGMII_DPSEL_INIT(port), DP_SEL);
/* config single link */
phytium_phy_writel(phytium_dp, PE220X_PHY_PLL_CFG(port), SINGLE_LINK);
/* pipe reset */
phytium_phy_writel(phytium_dp, PE220X_PHY_PIPE_RESET(port), RESET_DEASSERT);
mask = PLL0_LOCK_DONE;
do {
mdelay(1);
timeout--;
tmp = phytium_phy_readl(phytium_dp, PE220X_PHY_PMA_CONTROL2(port));
} while ((!(tmp & mask)) && timeout);
if (timeout == 0) {
DRM_ERROR("reset dp(%d) phy failed\n", port);
ret = -1;
}
udelay(1);
return ret;
}
static void pe220x_dp_hw_poweron_panel(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
int ret = 0;
phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | PANEL_POWER_ENABLE,
0, PE220X_DC_CMD_REGISTER(port));
ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(port),
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to poweron panel\n", __func__);
}
static void pe220x_dp_hw_poweroff_panel(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
int ret = 0;
phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | PANEL_POWER_DISABLE,
0, PE220X_DC_CMD_REGISTER(port));
ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(port),
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to poweroff panel\n", __func__);
}
static void pe220x_dp_hw_enable_backlight(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port, ret = 0;
phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | BACKLIGHT_ENABLE,
0, PE220X_DC_CMD_REGISTER(port));
ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(port),
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to enable backlight\n", __func__);
}
static void pe220x_dp_hw_disable_backlight(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
int ret = 0;
phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | BACKLIGHT_DISABLE,
0, PE220X_DC_CMD_REGISTER(port));
ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(port),
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to disable backlight\n", __func__);
}
static uint32_t pe220x_dp_hw_get_backlight(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int config;
uint32_t group_offset = priv->address_transform_base;
config = phytium_readl_reg(priv, group_offset, PE220X_DC_ADDRESS_TRANSFORM_BACKLIGHT_VALUE);
return ((config >> BACKLIGHT_VALUE_SHIFT) & BACKLIGHT_VALUE_MASK);
}
static int pe220x_dp_hw_set_backlight(struct phytium_dp_device *phytium_dp, uint32_t level)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
int config = 0;
int ret = 0;
if (level > PE220X_DP_BACKLIGHT_MAX) {
ret = -EINVAL;
goto out;
}
config = FLAG_REQUEST | CMD_BACKLIGHT | ((level & BACKLIGHT_MASK) << BACKLIGHT_SHIFT);
phytium_writel_reg(priv, config, 0, PE220X_DC_CMD_REGISTER(port));
ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(port),
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to set backlight\n", __func__);
out:
return ret;
}
bool pe220x_dp_hw_spread_is_enable(struct phytium_dp_device *phytium_dp)
{
return false;
}
int pe220x_dp_hw_reset(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
uint32_t group_offset = priv->dp_reg_base[port];
phytium_writel_reg(priv, DP_RESET, group_offset, PE220X_DP_CONTROLLER_RESET);
udelay(500);
phytium_writel_reg(priv, AUX_CLK_DIVIDER_100, group_offset, PHYTIUM_DP_AUX_CLK_DIVIDER);
phytium_writel_reg(priv, SUPPORT_EDP_1_4, group_offset, PHYTIUM_EDP_CRC_ENABLE);
return 0;
}
uint8_t pe220x_dp_hw_get_source_lane_count(struct phytium_dp_device *phytium_dp)
{
return pe220x_dp_source_lane_count[phytium_dp->port];
}
static struct phytium_dp_func pe220x_dp_funcs = {
.dp_hw_get_source_lane_count = pe220x_dp_hw_get_source_lane_count,
.dp_hw_reset = pe220x_dp_hw_reset,
.dp_hw_spread_is_enable = pe220x_dp_hw_spread_is_enable,
.dp_hw_set_backlight = pe220x_dp_hw_set_backlight,
.dp_hw_get_backlight = pe220x_dp_hw_get_backlight,
.dp_hw_disable_backlight = pe220x_dp_hw_disable_backlight,
.dp_hw_enable_backlight = pe220x_dp_hw_enable_backlight,
.dp_hw_poweroff_panel = pe220x_dp_hw_poweroff_panel,
.dp_hw_poweron_panel = pe220x_dp_hw_poweron_panel,
.dp_hw_init_phy = pe220x_dp_hw_init_phy,
.dp_hw_set_phy_lane_setting = pe220x_dp_hw_set_phy_lane_setting,
.dp_hw_set_phy_lane_and_rate = pe220x_dp_hw_set_phy_lane_and_rate,
};
void pe220x_dp_func_register(struct phytium_dp_device *phytium_dp)
{
phytium_dp->funcs = &pe220x_dp_funcs;
}

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Phytium display port DRM driver
*
* Copyright (C) 2021 Phytium Technology Co., Ltd.
*/
#ifndef __PE220X_DP_H__
#define __PE220X_DP_H__
#define PE220X_DP_BACKLIGHT_MAX 100
void pe220x_dp_func_register(struct phytium_dp_device *phytium_dp);
#endif /* __PE220X_DP_H__ */

View File

@ -0,0 +1,209 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Phytium Pe220x display engine register
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PE220X_REG_H__
#define __PE220X_REG_H__
#include "phytium_reg.h"
/* dc register */
#define PE220X_DC_CLOCK_CONTROL 0x0000
#define DC1_CORE_RESET (1<<18)
#define DC0_CORE_RESET (1<<17)
#define AXI_RESET (1<<16)
#define AHB_RESET (1<<12)
#define PE220X_DC_CMD_REGISTER(pipe) (PE220X_DC_BASE(0) + 0x00F0 + 0x4*(pipe))
#define FLAG_REPLY (1<<31)
#define FLAG_REQUEST (1<<30)
#define CMD_PIXEL_CLOCK (0x0 << 28)
#define CMD_BACKLIGHT (0x1 << 28)
#define CMD_DC_DP_RESET (0x3 << 28)
#define BACKLIGHT_SHIFT 21
#define BACKLIGHT_MASK 0x7f
#define BACKLIGHT_MAX 100
#define BACKLIGHT_ENABLE (101 << BACKLIGHT_SHIFT)
#define BACKLIGHT_DISABLE (102 << BACKLIGHT_SHIFT)
#define PANEL_POWER_ENABLE (103 << BACKLIGHT_SHIFT)
#define PANEL_POWER_DISABLE (104 << BACKLIGHT_SHIFT)
#define PIXEL_CLOCK_MASK (0x1fffff)
#define PE220X_DC_FRAMEBUFFER_Y_HI_ADDRESS 0x1404
#define PREFIX_MASK 0xff
#define PREFIX_SHIFT 32
#define PE220X_DC_CURSOR_HI_ADDRESS 0x1490
#define CURSOR_PREFIX_MASK 0xff
#define CURSOR_PREFIX_SHIFT 32
#define PE220X_DC_FRAMEBUFFER_U_HI_ADDRESS 0x1534
#define U_PREFIX_MASK 0xff
#define U_PREFIX_SHIFT 32
#define PE220X_DC_FRAMEBUFFER_V_HI_ADDRESS 0x153c
#define V_PREFIX_MASK 0xff
#define V_PREFIX_SHIFT 32
/* dp register */
#define PE220X_DP_CONTROLLER_RESET 0x0850
#define DP_RESET 0x1
/* address transform register */
#define PE220X_DC_ADDRESS_TRANSFORM_SRC_ADDR 0x0
#define SRC_ADDR_OFFSET 22
#define SRC_ADDR_MASK 0xffffffffff
#define PE220X_DC_ADDRESS_TRANSFORM_SIZE 0x4
#define ADDRESS_TRANSFORM_ENABLE (0x1 << 31)
#define SIZE_OFFSET 22
#define PE220X_DC_ADDRESS_TRANSFORM_DST_ADDR 0x8
#define DST_ADDR_OFFSET 22
#define PE220X_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS 0x48
#define DC_DP_RESET_STATUS(pipe) (1 << pipe)
#define DP_SPREAD_ENABLE(pipe) (0x8 << pipe)
#define PE220X_DC_ADDRESS_TRANSFORM_BACKLIGHT_VALUE 0x4c
#define BACKLIGHT_VALUE_MASK (0x7f)
#define BACKLIGHT_VALUE_SHIFT 16
/* phy register start */
#define PE220X_PHY_BASE(pipe) (0x100000*pipe)
#define PE220X_PHY_PIPE_RESET(pipe) (PE220X_PHY_BASE(pipe) + 0x40254)
#define RESET 0x0
#define RESET_DEASSERT 0x1
#define PE220X_PHY_MODE(pipe) (PE220X_PHY_BASE(pipe) + 0x40034)
#define LANE_BIT (0x3)
#define LANE_BIT_SHIFT 0x2
#define PE220X_PHY_LINK_CFG(pipe) (PE220X_PHY_BASE(pipe) + 0x40044)
#define LANE_MASTER 0x1
#define LANE_MASTER_SHIFT 1
#define PE220X_PHY_PLL_EN(pipe) (PE220X_PHY_BASE(pipe) + 0x40214)
#define PLL_EN 0x1
#define PLL_EN_SHIFT 1
#define PE220X_PHY_PMA_WIDTH(pipe) (PE220X_PHY_BASE(pipe) + 0x4021c)
#define BIT_20 0x5
#define BIT_20_SHIFT 4
#define PE220X_PHY_PLL_SOURCE_SEL(pipe) (PE220X_PHY_BASE(pipe) + 0x4004C)
#define PE220X_PHY_PMA0_POWER(pipe) (PE220X_PHY_BASE(pipe) + 0x402bc)
#define A0_ACTIVE 0x1
#define A0_ACTIVE_SHIFT 8
#define A3_POWERDOWN3 0x8
#define A3_POWERDOWN3_SHIFT 8
#define PE220X_PHY_LINK_RESET(pipe) (PE220X_PHY_BASE(pipe) + 0x40258)
#define LINK_RESET 0x1
#define LINK_RESET_MASK 0x1
#define LINTK_RESET_SHIFT 0x1
#define PE220X_PHY_SGMII_DPSEL_INIT(pipe) (PE220X_PHY_BASE(pipe) + 0x40260)
#define DP_SEL 0x1
#define PE220X_PHY_APB_RESET(pipe) (PE220X_PHY_BASE(pipe) + 0x40250)
#define APB_RESET 0x1
/* phy origin register */
#define PE220X_PHY_PLL_CFG(pipe) (PE220X_PHY_BASE(pipe) + 0x30038)
#define SINGLE_LINK 0x0
#define PE220X_PHY_PMA_CONTROL(pipe) (PE220X_PHY_BASE(pipe) + 0x3800c)
#define CONTROL_ENABLE 0x1
#define CONTROL_ENABLE_MASK 0x1
#define CONTROL_ENABLE_SHIFT 0x1
#define PE220X_PHY_PMA_CONTROL2(pipe) (PE220X_PHY_BASE(pipe) + 0x38004)
#define PLL0_LOCK_DONE (0x1 << 6)
#define PE220X_PHY_PLL0_CLK_SEL(pipe) (PE220X_PHY_BASE(pipe) + 0X684)
#define PLL_LINK_RATE_162000 0xf01
#define PLL_LINK_RATE_270000 0x701
#define PLL_LINK_RATE_540000 0x301
#define PLL_LINK_RATE_810000 0x200
#define PE220X_PHY_HSCLK0_SEL(pipe) (PE220X_PHY_BASE(pipe) + 0x18398)
#define HSCLK_LINK_0 0x0
#define HSCLK_LINK_1 0x1
#define PE220X_PHY_HSCLK0_DIV(pipe) (PE220X_PHY_BASE(pipe) + 0x1839c)
#define HSCLK_LINK_RATE_162000 0x2
#define HSCLK_LINK_RATE_270000 0x1
#define HSCLK_LINK_RATE_540000 0x0
#define HSCLK_LINK_RATE_810000 0x0
#define PE220X_PHY_PLLDRC0_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x18394)
#define PLLDRC_LINK0 0x1
#define PLLDRC_LINK1 0x9
#define PE220X_PHY_PLL0_DSM_M0(pipe) (PE220X_PHY_BASE(pipe) + 0x250)
#define PLL0_DSM_M0 0x4
#define PE220X_PHY_PLL0_VCOCAL_START(pipe) (PE220X_PHY_BASE(pipe) + 0x218)
#define PLL0_VCOCAL_START 0xc5e
#define PE220X_PHY_PLL0_VCOCAL_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x208)
#define PLL0_VCOCAL_CTRL 0x3
#define PE220X_PHY_PLL0_CP_PADJ(pipe) (PE220X_PHY_BASE(pipe) + 0x690)
#define PE220X_PHY_PLL0_CP_IADJ(pipe) (PE220X_PHY_BASE(pipe) + 0x694)
#define PE220X_PHY_PLL0_CP_FILT_PADJ(pipe) (PE220X_PHY_BASE(pipe) + 0x698)
#define PE220X_PHY_PLL0_INTDIV(pipe) (PE220X_PHY_BASE(pipe) + 0x240)
#define PE220X_PHY_PLL0_FRACDIVL(pipe) (PE220X_PHY_BASE(pipe) + 0x244)
#define PE220X_PHY_PLL0_FRACDIVH(pipe) (PE220X_PHY_BASE(pipe) + 0x248)
#define PE220X_PHY_PLL0_HIGH_THR(pipe) (PE220X_PHY_BASE(pipe) + 0x24c)
#define PE220X_PHY_PLL0_PDIAG_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x680)
#define PE220X_PHY_PLL0_VCOCAL_PLLCNT_START(pipe) (PE220X_PHY_BASE(pipe) + 0x220)
#define PE220X_PHY_PLL0_LOCK_PEFCNT(pipe) (PE220X_PHY_BASE(pipe) + 0x270)
#define PE220X_PHY_PLL0_LOCK_PLLCNT_START(pipe) (PE220X_PHY_BASE(pipe) + 0x278)
#define PE220X_PHY_PLL0_LOCK_PLLCNT_THR(pipe) (PE220X_PHY_BASE(pipe) + 0x27c)
#define PE220X_PHY_PLL0_TX_PSC_A0(pipe) (PE220X_PHY_BASE(pipe) + 0x18400)
#define PLL0_TX_PSC_A0 0xfb
#define PE220X_PHY_PLL0_TX_PSC_A2(pipe) (PE220X_PHY_BASE(pipe) + 0x18408)
#define PLL0_TX_PSC_A2 0x4aa
#define PE220X_PHY_PLL0_TX_PSC_A3(pipe) (PE220X_PHY_BASE(pipe) + 0x1840c)
#define PLL0_TX_PSC_A3 0x4aa
#define PE220X_PHY_PLL0_RX_PSC_A0(pipe) (PE220X_PHY_BASE(pipe) + 0x28000)
#define PLL0_RX_PSC_A0 0x0
#define PE220X_PHY_PLL0_RX_PSC_A2(pipe) (PE220X_PHY_BASE(pipe) + 0x28008)
#define PLL0_RX_PSC_A2 0x0
#define PE220X_PHY_PLL0_RX_PSC_A3(pipe) (PE220X_PHY_BASE(pipe) + 0x2800C)
#define PLL0_RX_PSC_A3 0x0
#define PE220X_PHY_PLL0_RX_PSC_CAL(pipe) (PE220X_PHY_BASE(pipe) + 0x28018)
#define PLL0_RX_PSC_CAL 0x0
#define PE220X_PHY_PLL0_XCVR_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x183a8)
#define PLL0_XCVR_CTRL 0xf
#define PE220X_PHY_PLL0_RX_GCSM1_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x28420)
#define PLL0_RX_GCSM1_CTRL 0x0
#define PE220X_PHY_PLL0_RX_GCSM2_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x28440)
#define PLL0_RX_GCSM2_CTRL 0x0
#define PE220X_PHY_PLL0_RX_PERGCSM_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x28460)
#define PLL0_RX_PERGCSM_CTRL 0x0
/* swing and emphasis */
#define PE220X_PHY_PLL0_TX_DIAG_ACYA(pipe) (PE220X_PHY_BASE(pipe) + 0x1879c)
#define LOCK 1
#define UNLOCK 0
#define PE220X_PHY_PLL0_TX_TXCC_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x18100)
#define TX_TXCC_CTRL 0x8a4
#define PE220X_PHY_PLL0_TX_DRV(pipe) (PE220X_PHY_BASE(pipe) + 0x18318)
#define TX_DRV 0x3
#define PE220X_PHY_PLL0_TX_MGNFS(pipe) (PE220X_PHY_BASE(pipe) + 0x18140)
#define PE220X_PHY_PLL0_TX_CPOST(pipe) (PE220X_PHY_BASE(pipe) + 0x18130)
#endif /* __PE220X_REG_H__ */

View File

@ -0,0 +1,763 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display 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 <drm/drm_vblank.h>
#include "phytium_display_drv.h"
#include "phytium_crtc.h"
#include "phytium_plane.h"
#include "phytium_dp.h"
#include "px210_dc.h"
#include "pe220x_dc.h"
#include "phytium_reg.h"
#define MAXKERNELSIZE 9
#define SUBPIXELINDEXBITS 5
#define SUBPIXELCOUNT (1 << SUBPIXELINDEXBITS)
#define SUBPIXELLOADCOUNT (SUBPIXELCOUNT / 2 + 1)
#define WEIGHTSTATECOUNT (((SUBPIXELLOADCOUNT * MAXKERNELSIZE + 1) & ~1) / 2)
#define KERNELTABLESIZE (SUBPIXELLOADCOUNT * MAXKERNELSIZE * sizeof(uint16_t))
#define PHYALIGN(n, align) (((n) + ((align) - 1)) & ~((align) - 1))
#define KERNELSTATES (PHYALIGN(KERNELTABLESIZE + 4, 8))
#define PHYPI 3.14159265358979323846f
#define MATH_Add(X, Y) (float)((X) + (Y))
#define MATH_Multiply(X, Y) (float)((X) * (Y))
#define MATH_Divide(X, Y) (float)((X) / (Y))
#define MATH_DivideFromUInteger(X, Y) ((float)(X) / (float)(Y))
#define MATH_I2Float(X) (float)(X)
struct filter_blit_array {
uint8_t kernelSize;
uint32_t scaleFactor;
uint32_t *kernelStates;
};
static uint32_t dc_scaling_get_factor(uint32_t src_size, uint32_t dst_size)
{
uint32_t factor = 0;
factor = ((src_size - 1) << SCALE_FACTOR_SRC_OFFSET) / (dst_size - 1);
return factor;
}
static float dc_sint(float x)
{
const float B = 1.2732395477;
const float C = -0.4052847346;
const float P = 0.2310792853;
float y;
if (x < 0)
y = B*x - C*x*x;
else
y = B*x + C*x*x;
if (y < 0)
y = P * (y * (0 - y) - y) + y;
else
y = P * (y * y - y) + y;
return y;
}
static float dc_sinc_filter(float x, int radius)
{
float pit, pitd, f1, f2, result;
float f_radius = MATH_I2Float(radius);
if (x == 0.0f) {
result = 1.0f;
} else if ((x < -f_radius) || (x > f_radius)) {
result = 0.0f;
} else {
pit = MATH_Multiply(PHYPI, x);
pitd = MATH_Divide(pit, f_radius);
f1 = MATH_Divide(dc_sint(pit), pit);
f2 = MATH_Divide(dc_sint(pitd), pitd);
result = MATH_Multiply(f1, f2);
}
return result;
}
static int dc_calculate_sync_table(
uint8_t kernel_size,
uint32_t src_size,
uint32_t dst_size,
struct filter_blit_array *kernel_info)
{
uint32_t scale_factor;
float f_scale;
int kernel_half;
float f_subpixel_step;
float f_subpixel_offset;
uint32_t subpixel_pos;
int kernel_pos;
int padding;
uint16_t *kernel_array;
int range = 0;
do {
/* Compute the scale factor. */
scale_factor = dc_scaling_get_factor(src_size, dst_size);
/* Same kernel size and ratio as before? */
if ((kernel_info->kernelSize == kernel_size) &&
(kernel_info->scaleFactor == kernel_size)) {
break;
}
/* check the array */
if (kernel_info->kernelStates == NULL)
break;
/* Store new parameters. */
kernel_info->kernelSize = kernel_size;
kernel_info->scaleFactor = scale_factor;
/* Compute the scale factor. */
f_scale = MATH_DivideFromUInteger(dst_size, src_size);
/* Adjust the factor for magnification. */
if (f_scale > 1.0f)
f_scale = 1.0f;
/* Calculate the kernel half. */
kernel_half = (int) (kernel_info->kernelSize >> 1);
/* Calculate the subpixel step. */
f_subpixel_step = MATH_Divide(1.0f, MATH_I2Float(SUBPIXELCOUNT));
/* Init the subpixel offset. */
f_subpixel_offset = 0.5f;
/* Determine kernel padding size. */
padding = (MAXKERNELSIZE - kernel_info->kernelSize) / 2;
/* Set initial kernel array pointer. */
kernel_array = (uint16_t *) (kernel_info->kernelStates + 1);
/* Loop through each subpixel. */
for (subpixel_pos = 0; subpixel_pos < SUBPIXELLOADCOUNT; subpixel_pos++) {
/* Define a temporary set of weights. */
float fSubpixelSet[MAXKERNELSIZE];
/* Init the sum of all weights for the current subpixel. */
float fWeightSum = 0.0f;
uint16_t weightSum = 0;
short int adjustCount, adjustFrom;
short int adjustment;
/* Compute weights. */
for (kernel_pos = 0; kernel_pos < MAXKERNELSIZE; kernel_pos++) {
/* Determine the current index. */
int index = kernel_pos - padding;
/* Pad with zeros. */
if ((index < 0) || (index >= kernel_info->kernelSize)) {
fSubpixelSet[kernel_pos] = 0.0f;
} else {
if (kernel_info->kernelSize == 1) {
fSubpixelSet[kernel_pos] = 1.0f;
} else {
/* Compute the x position for filter function. */
float fX = MATH_Add(
MATH_I2Float(index - kernel_half),
f_subpixel_offset);
fX = MATH_Multiply(fX, f_scale);
/* Compute the weight. */
fSubpixelSet[kernel_pos] = dc_sinc_filter(fX,
kernel_half);
}
/* Update the sum of weights. */
fWeightSum = MATH_Add(fWeightSum,
fSubpixelSet[kernel_pos]);
}
}
/* Adjust weights so that the sum will be 1.0. */
for (kernel_pos = 0; kernel_pos < MAXKERNELSIZE; kernel_pos++) {
/* Normalize the current weight. */
float fWeight = MATH_Divide(fSubpixelSet[kernel_pos],
fWeightSum);
/* Convert the weight to fixed point and store in the table. */
if (fWeight == 0.0f)
kernel_array[kernel_pos] = 0x0000;
else if (fWeight >= 1.0f)
kernel_array[kernel_pos] = 0x4000;
else if (fWeight <= -1.0f)
kernel_array[kernel_pos] = 0xC000;
else
kernel_array[kernel_pos] =
(int16_t) MATH_Multiply(fWeight, 16384.0f);
weightSum += kernel_array[kernel_pos];
}
/* Adjust the fixed point coefficients. */
adjustCount = 0x4000 - weightSum;
if (adjustCount < 0) {
adjustCount = -adjustCount;
adjustment = -1;
} else {
adjustment = 1;
}
adjustFrom = (MAXKERNELSIZE - adjustCount) / 2;
for (kernel_pos = 0; kernel_pos < adjustCount; kernel_pos++) {
range = (MAXKERNELSIZE*subpixel_pos + adjustFrom + kernel_pos) *
sizeof(uint16_t);
if ((range >= 0) && (range < KERNELTABLESIZE))
kernel_array[adjustFrom + kernel_pos] += adjustment;
else
DRM_ERROR("%s failed\n", __func__);
}
kernel_array += MAXKERNELSIZE;
/* Advance to the next subpixel. */
f_subpixel_offset = MATH_Add(f_subpixel_offset, -f_subpixel_step);
}
} while (0);
return 0;
}
static void phytium_dc_scaling_config(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct drm_device *dev = crtc->dev;
struct phytium_display_private *priv = dev->dev_private;
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc);
int phys_pipe = phytium_crtc->phys_pipe;
uint32_t group_offset = priv->dc_reg_base[phys_pipe];
uint32_t scale_factor_x, scale_factor_y, i;
uint32_t kernelStates[128];
struct filter_blit_array kernel_info_width;
void *tmp = NULL;
if (mode->hdisplay != mode->crtc_hdisplay || mode->vdisplay != mode->crtc_vdisplay) {
phytium_crtc->src_width = mode->hdisplay;
phytium_crtc->src_height = mode->vdisplay;
phytium_crtc->dst_width = mode->crtc_hdisplay;
phytium_crtc->dst_height = mode->crtc_vdisplay;
phytium_crtc->dst_x = (mode->crtc_hdisplay - phytium_crtc->dst_width) / 2;
phytium_crtc->dst_y = (mode->crtc_vdisplay - phytium_crtc->dst_height) / 2;
scale_factor_x = dc_scaling_get_factor(phytium_crtc->src_width,
phytium_crtc->dst_width);
scale_factor_y = dc_scaling_get_factor(phytium_crtc->src_height,
phytium_crtc->dst_height);
if (scale_factor_y > (SCALE_FACTOR_Y_MAX << SCALE_FACTOR_SRC_OFFSET))
scale_factor_y = (SCALE_FACTOR_Y_MAX << SCALE_FACTOR_SRC_OFFSET);
phytium_writel_reg(priv, scale_factor_x & SCALE_FACTOR_X_MASK,
group_offset, PHYTIUM_DC_FRAMEBUFFER_SCALE_FACTOR_X);
phytium_writel_reg(priv, scale_factor_y & SCALE_FACTOR_Y_MASK,
group_offset, PHYTIUM_DC_FRAMEBUFFER_SCALE_FACTOR_Y);
phytium_writel_reg(priv, FRAMEBUFFER_TAP,
group_offset, PHYTIUM_DC_FRAMEBUFFER_SCALECONFIG);
tmp = kmalloc(KERNELSTATES, GFP_KERNEL);
if (!tmp) {
DRM_ERROR("malloc %ld failed\n", KERNELSTATES);
return;
}
memset(&kernel_info_width, 0, sizeof(struct filter_blit_array));
kernel_info_width.kernelStates = tmp;
memset(kernel_info_width.kernelStates, 0, KERNELSTATES);
kernel_neon_begin();
dc_calculate_sync_table(FRAMEBUFFER_HORIZONTAL_FILTER_TAP,
phytium_crtc->src_width,
phytium_crtc->dst_width,
&kernel_info_width);
memset(kernelStates, 0, sizeof(kernelStates));
memcpy(kernelStates, kernel_info_width.kernelStates + 1, KERNELSTATES - 4);
kernel_neon_end();
phytium_writel_reg(priv, HORI_FILTER_INDEX,
group_offset, PHYTIUM_DC_FRAMEBUFFER_HORI_FILTER_INDEX);
for (i = 0; i < 128; i++) {
phytium_writel_reg(priv, kernelStates[i],
group_offset, PHYTIUM_DC_FRAMEBUFFER_HORI_FILTER);
}
memset(&kernel_info_width, 0, sizeof(struct filter_blit_array));
kernel_info_width.kernelStates = tmp;
memset(kernel_info_width.kernelStates, 0, KERNELSTATES);
kernel_neon_begin();
dc_calculate_sync_table(FRAMEBUFFER_FILTER_TAP, phytium_crtc->src_height,
phytium_crtc->dst_height, &kernel_info_width);
memset(kernelStates, 0, sizeof(kernelStates));
memcpy(kernelStates, kernel_info_width.kernelStates + 1, KERNELSTATES - 4);
kernel_neon_end();
phytium_writel_reg(priv, VERT_FILTER_INDEX,
group_offset, PHYTIUM_DC_FRAMEBUFFER_VERT_FILTER_INDEX);
for (i = 0; i < 128; i++)
phytium_writel_reg(priv, kernelStates[i],
group_offset, PHYTIUM_DC_FRAMEBUFFER_VERT_FILTER);
phytium_writel_reg(priv, INITIALOFFSET,
group_offset, PHYTIUM_DC_FRAMEBUFFER_INITIALOFFSET);
kfree(tmp);
phytium_crtc->scale_enable = true;
} else {
phytium_crtc->scale_enable = false;
}
}
static void phytium_crtc_gamma_set(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 phys_pipe = phytium_crtc->phys_pipe;
uint32_t group_offset = priv->dc_reg_base[phys_pipe];
uint32_t config = 0;
struct drm_crtc_state *state = crtc->state;
struct drm_color_lut *lut;
int i;
if (state->gamma_lut) {
if (WARN((state->gamma_lut->length/sizeof(struct drm_color_lut) != GAMMA_INDEX_MAX),
"gamma size is not match\n"))
return;
lut = (struct drm_color_lut *)state->gamma_lut->data;
for (i = 0; i < GAMMA_INDEX_MAX; i++) {
phytium_writel_reg(priv, i, group_offset, PHYTIUM_DC_GAMMA_INDEX);
config = ((lut[i].red >> 6) & GAMMA_RED_MASK) << GAMMA_RED_SHIFT;
config |= (((lut[i].green >> 6) & GAMMA_GREEN_MASK) << GAMMA_GREEN_SHIFT);
config |= (((lut[i].blue >> 6) & GAMMA_BLUE_MASK) << GAMMA_BLUE_SHIFT);
phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_GAMMA_DATA);
}
}
}
static void phytium_crtc_gamma_init(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 phys_pipe = phytium_crtc->phys_pipe;
uint32_t group_offset = priv->dc_reg_base[phys_pipe];
uint32_t config = 0;
uint16_t *red, *green, *blue;
int i;
if (WARN((crtc->gamma_size != GAMMA_INDEX_MAX), "gamma size is not match\n"))
return;
red = crtc->gamma_store;
green = red + crtc->gamma_size;
blue = green + crtc->gamma_size;
for (i = 0; i < GAMMA_INDEX_MAX; i++) {
phytium_writel_reg(priv, i, group_offset, PHYTIUM_DC_GAMMA_INDEX);
config = ((*red++ >> 6) & GAMMA_RED_MASK) << GAMMA_RED_SHIFT;
config |= (((*green++ >> 6) & GAMMA_GREEN_MASK) << GAMMA_GREEN_SHIFT);
config |= (((*blue++ >> 6) & GAMMA_BLUE_MASK) << GAMMA_BLUE_SHIFT);
phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_GAMMA_DATA);
}
}
static void phytium_crtc_destroy(struct drm_crtc *crtc)
{
struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc);
drm_crtc_cleanup(crtc);
kfree(phytium_crtc);
}
struct drm_crtc_state *
phytium_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
{
struct phytium_crtc_state *phytium_crtc_state = NULL;
phytium_crtc_state = kmemdup(crtc->state, sizeof(*phytium_crtc_state),
GFP_KERNEL);
if (!phytium_crtc_state)
return NULL;
__drm_atomic_helper_crtc_duplicate_state(crtc,
&phytium_crtc_state->base);
return &phytium_crtc_state->base;
}
void
phytium_crtc_atomic_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct phytium_crtc_state *phytium_crtc_state =
to_phytium_crtc_state(state);
phytium_crtc_state = to_phytium_crtc_state(state);
__drm_atomic_helper_crtc_destroy_state(state);
kfree(phytium_crtc_state);
}
static int phytium_enable_vblank(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 phys_pipe = phytium_crtc->phys_pipe;
phytium_writel_reg(priv, INT_ENABLE, priv->dc_reg_base[phys_pipe], PHYTIUM_DC_INT_ENABLE);
return 0;
}
static void phytium_disable_vblank(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 phys_pipe = phytium_crtc->phys_pipe;
phytium_writel_reg(priv, INT_DISABLE, priv->dc_reg_base[phys_pipe],
PHYTIUM_DC_INT_ENABLE);
}
static const struct drm_crtc_funcs phytium_crtc_funcs = {
.gamma_set = drm_atomic_helper_legacy_gamma_set,
.set_config = drm_atomic_helper_set_config,
.destroy = phytium_crtc_destroy,
.page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = phytium_crtc_atomic_duplicate_state,
.atomic_destroy_state = phytium_crtc_atomic_destroy_state,
.enable_vblank = phytium_enable_vblank,
.disable_vblank = phytium_disable_vblank,
};
static void
phytium_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct drm_atomic_state *state = old_state->state;
struct drm_device *dev = crtc->dev;
struct phytium_display_private *priv = dev->dev_private;
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
struct drm_connector_state *new_conn_state;
struct drm_connector *conn;
struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc);
int phys_pipe = phytium_crtc->phys_pipe;
uint32_t group_offset = priv->dc_reg_base[phys_pipe];
int config = 0, i = 0;
for_each_new_connector_in_state(state, conn, new_conn_state, i) {
if (new_conn_state->crtc != crtc)
continue;
switch (conn->display_info.bpc) {
case 10:
phytium_crtc->bpc = DP_RGB101010;
break;
case 6:
phytium_crtc->bpc = DP_RGB666;
break;
default:
phytium_crtc->bpc = DP_RGB888;
break;
}
}
/* config pix clock */
phytium_crtc->dc_hw_config_pix_clock(crtc, mode->clock);
phytium_dc_scaling_config(crtc, old_state);
config = ((mode->crtc_hdisplay & HDISPLAY_END_MASK) << HDISPLAY_END_SHIFT)
| ((mode->crtc_htotal&HDISPLAY_TOTAL_MASK) << HDISPLAY_TOTAL_SHIFT);
phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_HDISPLAY);
config = ((mode->crtc_hsync_start & HSYNC_START_MASK) << HSYNC_START_SHIFT)
| ((mode->crtc_hsync_end & HSYNC_END_MASK) << HSYNC_END_SHIFT)
| HSYNC_PULSE_ENABLED;
config |= (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : HSYNC_NEGATIVE;
phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_HSYNC);
config = ((mode->crtc_vdisplay & VDISPLAY_END_MASK) << VDISPLAY_END_SHIFT)
| ((mode->crtc_vtotal & VDISPLAY_TOTAL_MASK) << VDISPLAY_TOTAL_SHIFT);
phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_VDISPLAY);
config = ((mode->crtc_vsync_start & VSYNC_START_MASK) << VSYNC_START_SHIFT)
| ((mode->crtc_vsync_end & VSYNC_END_MASK) << VSYNC_END_SHIFT)
| VSYNC_PULSE_ENABLED;
config |= (mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : VSYNC_NEGATIVE;
phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_VSYNC);
config = PANEL_DATAENABLE_ENABLE | PANEL_DATA_ENABLE | PANEL_CLOCK_ENABLE;
phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_PANEL_CONFIG);
config = phytium_crtc->bpc | OUTPUT_DP;
phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_DP_CONFIG);
config = phytium_readl_reg(priv, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG);
if (crtc->state->active)
config |= FRAMEBUFFER_OUTPUT | FRAMEBUFFER_RESET;
else
config &= (~(FRAMEBUFFER_OUTPUT | FRAMEBUFFER_RESET));
if (phytium_crtc->scale_enable)
config |= FRAMEBUFFER_SCALE_ENABLE;
else
config &= (~FRAMEBUFFER_SCALE_ENABLE);
if (crtc->state->gamma_lut)
phytium_crtc_gamma_set(crtc);
else
phytium_crtc_gamma_init(crtc);
phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG);
drm_crtc_vblank_on(crtc);
}
static void
phytium_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc);
drm_crtc_vblank_off(crtc);
phytium_crtc->dc_hw_disable(crtc);
}
static void phytium_crtc_update_timing_for_drm_display_mode(struct drm_display_mode *drm_mode,
const struct drm_display_mode *native_mode)
{
if (native_mode->clock == drm_mode->clock &&
native_mode->htotal == drm_mode->htotal &&
native_mode->vtotal == drm_mode->vtotal) {
drm_mode->crtc_hdisplay = native_mode->crtc_hdisplay;
drm_mode->crtc_vdisplay = native_mode->crtc_vdisplay;
drm_mode->crtc_clock = native_mode->crtc_clock;
drm_mode->crtc_hblank_start = native_mode->crtc_hblank_start;
drm_mode->crtc_hblank_end = native_mode->crtc_hblank_end;
drm_mode->crtc_hsync_start = native_mode->crtc_hsync_start;
drm_mode->crtc_hsync_end = native_mode->crtc_hsync_end;
drm_mode->crtc_htotal = native_mode->crtc_htotal;
drm_mode->crtc_hskew = native_mode->crtc_hskew;
drm_mode->crtc_vblank_start = native_mode->crtc_vblank_start;
drm_mode->crtc_vblank_end = native_mode->crtc_vblank_end;
drm_mode->crtc_vsync_start = native_mode->crtc_vsync_start;
drm_mode->crtc_vsync_end = native_mode->crtc_vsync_end;
drm_mode->crtc_vtotal = native_mode->crtc_vtotal;
}
}
static int
phytium_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state)
{
struct drm_atomic_state *state = crtc_state->state;
struct drm_plane_state *new_plane_state = NULL;
int ret = 0;
struct drm_connector *connector;
struct drm_connector_state *new_con_state;
uint32_t i;
struct phytium_dp_device *phytium_dp = NULL;
for_each_new_connector_in_state(state, connector, new_con_state, i) {
if (new_con_state->crtc == crtc) {
phytium_dp = connector_to_dp_device(connector);
break;
}
}
if (phytium_dp)
phytium_crtc_update_timing_for_drm_display_mode(&crtc_state->adjusted_mode,
&phytium_dp->native_mode);
new_plane_state = drm_atomic_get_new_plane_state(crtc_state->state,
crtc->primary);
if (crtc_state->enable && new_plane_state && !new_plane_state->crtc) {
ret = -EINVAL;
goto fail;
}
return 0;
fail:
return ret;
}
static void
phytium_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
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, config;
uint32_t group_offset = priv->dc_reg_base[phys_pipe];
config = phytium_readl_reg(priv, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG);
if (config & FRAMEBUFFER_RESET) {
phytium_writel_reg(priv, config | FRAMEBUFFER_VALID_PENDING,
group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG);
}
}
static void phytium_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
struct drm_device *dev = crtc->dev;
struct phytium_display_private *priv = dev->dev_private;
struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc);
struct phytium_crtc_state *phytium_crtc_state = NULL;
int phys_pipe = phytium_crtc->phys_pipe, config;
uint32_t group_offset = priv->dc_reg_base[phys_pipe];
DRM_DEBUG_KMS("crtc->state active:%d enable:%d\n",
crtc->state->active, crtc->state->enable);
phytium_crtc_state = to_phytium_crtc_state(crtc->state);
if (crtc->state->color_mgmt_changed)
phytium_crtc_gamma_set(crtc);
config = phytium_readl_reg(priv, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG);
phytium_writel_reg(priv, config&(~FRAMEBUFFER_VALID_PENDING),
group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG);
if (crtc->state->event) {
DRM_DEBUG_KMS("vblank->refcount:%d\n",
atomic_read(&dev->vblank[0].refcount));
spin_lock_irq(&dev->event_lock);
if (drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, crtc->state->event);
else
drm_crtc_send_vblank_event(crtc, crtc->state->event);
crtc->state->event = NULL;
spin_unlock_irq(&dev->event_lock);
}
}
static enum drm_mode_status
phytium_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
{
struct drm_device *dev = crtc->dev;
struct phytium_display_private *priv = dev->dev_private;
if (mode->crtc_clock > priv->info.crtc_clock_max)
return MODE_CLOCK_HIGH;
if (mode->hdisplay > priv->info.hdisplay_max)
return MODE_BAD_HVALUE;
if (mode->vdisplay > priv->info.vdisplay_max)
return MODE_BAD_VVALUE;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return MODE_NO_INTERLACE;
return MODE_OK;
}
static const struct drm_crtc_helper_funcs phytium_crtc_helper_funcs = {
.mode_valid = phytium_crtc_mode_valid,
.atomic_check = phytium_crtc_atomic_check,
.atomic_begin = phytium_crtc_atomic_begin,
.atomic_flush = phytium_crtc_atomic_flush,
.atomic_enable = phytium_crtc_atomic_enable,
.atomic_disable = phytium_crtc_atomic_disable,
};
void phytium_crtc_resume(struct drm_device *drm_dev)
{
struct drm_crtc *crtc;
struct phytium_crtc *phytium_crtc = NULL;
drm_for_each_crtc(crtc, drm_dev) {
phytium_crtc = to_phytium_crtc(crtc);
if (phytium_crtc->dc_hw_reset)
phytium_crtc->dc_hw_reset(crtc);
phytium_crtc_gamma_init(crtc);
}
}
int phytium_crtc_init(struct drm_device *dev, int phys_pipe)
{
struct phytium_crtc *phytium_crtc;
struct phytium_crtc_state *phytium_crtc_state;
struct phytium_plane *phytium_primary_plane = NULL;
struct phytium_plane *phytium_cursor_plane = NULL;
struct phytium_display_private *priv = dev->dev_private;
int ret;
phytium_crtc = kzalloc(sizeof(*phytium_crtc), GFP_KERNEL);
if (!phytium_crtc) {
ret = -ENOMEM;
goto failed_malloc_crtc;
}
phytium_crtc_state = kzalloc(sizeof(*phytium_crtc_state), GFP_KERNEL);
if (!phytium_crtc_state) {
ret = -ENOMEM;
goto failed_malloc_crtc_state;
}
phytium_crtc_state->base.crtc = &phytium_crtc->base;
phytium_crtc->base.state = &phytium_crtc_state->base;
phytium_crtc->phys_pipe = phys_pipe;
if (IS_PX210(priv)) {
phytium_crtc->dc_hw_config_pix_clock = px210_dc_hw_config_pix_clock;
phytium_crtc->dc_hw_disable = px210_dc_hw_disable;
phytium_crtc->dc_hw_reset = NULL;
priv->dc_reg_base[phys_pipe] = PX210_DC_BASE(phys_pipe);
priv->dcreq_reg_base[phys_pipe] = PX210_DCREQ_BASE(phys_pipe);
priv->address_transform_base = PX210_ADDRESS_TRANSFORM_BASE;
} else if (IS_PE220X(priv)) {
phytium_crtc->dc_hw_config_pix_clock = pe220x_dc_hw_config_pix_clock;
phytium_crtc->dc_hw_disable = pe220x_dc_hw_disable;
phytium_crtc->dc_hw_reset = pe220x_dc_hw_reset;
priv->dc_reg_base[phys_pipe] = PE220X_DC_BASE(phys_pipe);
priv->dcreq_reg_base[phys_pipe] = 0x0;
priv->address_transform_base = PE220X_ADDRESS_TRANSFORM_BASE;
}
phytium_primary_plane = phytium_primary_plane_create(dev, phys_pipe);
if (IS_ERR(phytium_primary_plane)) {
ret = PTR_ERR(phytium_primary_plane);
DRM_ERROR("create primary plane failed, phys_pipe(%d)\n", phys_pipe);
goto failed_create_primary;
}
phytium_cursor_plane = phytium_cursor_plane_create(dev, phys_pipe);
if (IS_ERR(phytium_cursor_plane)) {
ret = PTR_ERR(phytium_cursor_plane);
DRM_ERROR("create cursor plane failed, phys_pipe(%d)\n", phys_pipe);
goto failed_create_cursor;
}
ret = drm_crtc_init_with_planes(dev, &phytium_crtc->base,
&phytium_primary_plane->base,
&phytium_cursor_plane->base,
&phytium_crtc_funcs,
"phys_pipe %d", phys_pipe);
if (ret) {
DRM_ERROR("init crtc with plane failed, phys_pipe(%d)\n", phys_pipe);
goto failed_crtc_init;
}
drm_crtc_helper_add(&phytium_crtc->base, &phytium_crtc_helper_funcs);
drm_crtc_vblank_reset(&phytium_crtc->base);
drm_mode_crtc_set_gamma_size(&phytium_crtc->base, GAMMA_INDEX_MAX);
drm_crtc_enable_color_mgmt(&phytium_crtc->base, 0, false, GAMMA_INDEX_MAX);
if (phytium_crtc->dc_hw_reset)
phytium_crtc->dc_hw_reset(&phytium_crtc->base);
phytium_crtc_gamma_init(&phytium_crtc->base);
return 0;
failed_crtc_init:
failed_create_cursor:
/* drm_mode_config_cleanup() will free any crtcs/planes already initialized */
failed_create_primary:
kfree(phytium_crtc_state);
failed_malloc_crtc_state:
kfree(phytium_crtc);
failed_malloc_crtc:
return ret;
}

View File

@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_CRTC_H__
#define __PHYTIUM_CRTC_H__
struct phytium_crtc {
struct drm_crtc base;
int phys_pipe;
unsigned int bpc;
/* scale */
uint32_t src_width;
uint32_t src_height;
uint32_t dst_width;
uint32_t dst_height;
uint32_t dst_x;
uint32_t dst_y;
bool scale_enable;
bool reserve[3];
void (*dc_hw_config_pix_clock)(struct drm_crtc *crtc, int clock);
void (*dc_hw_disable)(struct drm_crtc *crtc);
void (*dc_hw_reset)(struct drm_crtc *crtc);
};
struct phytium_crtc_state {
struct drm_crtc_state base;
};
#define to_phytium_crtc(x) container_of(x, struct phytium_crtc, base)
#define to_phytium_crtc_state(x) container_of(x, struct phytium_crtc_state, base)
void phytium_crtc_resume(struct drm_device *drm_dev);
int phytium_crtc_init(struct drm_device *dev, int pipe);
#endif /* __PHYTIUM_CRTC_H__ */

View File

@ -0,0 +1,456 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <drm/drm_file.h>
#include "phytium_display_drv.h"
#include "phytium_dp.h"
#include "phytium_reg.h"
const char *const mem_state[PHYTIUM_MEM_STATE_TYPE_COUNT] = {
"Memory_Vram_Total",
"Memory_Vram_Alloc",
"Memory_System_Carveout_Total",
"Memory_System_Carveout_Alloc",
"Memory_System_Alloc",
};
static ssize_t
phytium_dp_register_write(struct file *filp,
const char __user *ubuf,
size_t len,
loff_t *ppos)
{
char tmp[16];
if (len >= sizeof(tmp))
return -EINVAL;
memset(tmp, 0, sizeof(tmp));
if (copy_from_user(tmp, ubuf, len))
return -EFAULT;
tmp[len] = '\0';
return len;
}
static int phytium_dp_register_show(struct seq_file *m, void *data)
{
struct drm_connector *connector = m->private;
struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector);
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
uint32_t group_offset = priv->dp_reg_base[port];
seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_M_VID,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_M_VID));
seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_N_VID,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_N_VID));
seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_TRANSFER_UNIT_SIZE,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_TRANSFER_UNIT_SIZE));
seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_DATA_COUNT,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_DATA_COUNT));
seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_MAIN_LINK_HTOTAL,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_HTOTAL));
seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_MAIN_LINK_HRES,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_HRES));
seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_MAIN_LINK_HSWIDTH,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_HSWIDTH));
seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_MAIN_LINK_HSTART,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_HSTART));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_VTOTAL,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_VTOTAL));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_VRES,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_VRES));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_VSWIDTH,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_VSWIDTH));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_VSTART,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_VSTART));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_POLARITY,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_POLARITY));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_MISC0,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_MISC0));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_MISC1,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_MISC1));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_USER_SYNC_POLARITY,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_USER_SYNC_POLARITY));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_VIDEO_STREAM_ENABLE,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_VIDEO_STREAM_ENABLE));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SECONDARY_STREAM_ENABLE,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SECONDARY_STREAM_ENABLE));
seq_puts(m, "audio:\n");
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_INPUT_SELECT,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_INPUT_SELECT));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_DIRECT_CLKDIV,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_DIRECT_CLKDIV));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CHANNEL_COUNT,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CHANNEL_COUNT));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CHANNEL_MAP,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CHANNEL_MAP));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_DATA_WINDOW,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_DATA_WINDOW));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CS_CATEGORY_CODE,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CS_CATEGORY_CODE));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_MAUD,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_MAUD));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_NAUD,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_NAUD));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CLOCK_MODE,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CLOCK_MODE));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CS_SOURCE_FORMAT,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CS_SOURCE_FORMAT));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CS_LENGTH_ORIG_FREQ,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CS_LENGTH_ORIG_FREQ));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CS_FREQ_CLOCK_ACCURACY,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CS_FREQ_CLOCK_ACCURACY));
seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_AUDIO_ENABLE,
phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_AUDIO_ENABLE));
return 0;
}
static int phytium_dp_register_open(struct inode *inode, struct file *file)
{
return single_open(file, phytium_dp_register_show, inode->i_private);
}
static const struct file_operations phytium_dp_register_fops = {
.owner = THIS_MODULE,
.open = phytium_dp_register_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = phytium_dp_register_write,
};
static ssize_t
phytium_dp_trigger_train_fail_write(struct file *filp,
const char __user *ubuf,
size_t len,
loff_t *ppos)
{
struct seq_file *m = filp->private_data;
struct drm_connector *connector = m->private;
struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector);
char tmp[16];
if (len >= sizeof(tmp))
return -EINVAL;
memset(tmp, 0, sizeof(tmp));
if (copy_from_user(tmp, ubuf, len))
return -EFAULT;
tmp[len] = '\0';
if (kstrtouint(tmp, 10, &phytium_dp->trigger_train_fail) != 0)
return -EINVAL;
return len;
}
static int phytium_dp_trigger_train_fail_show(struct seq_file *m, void *data)
{
struct drm_connector *connector = m->private;
struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector);
seq_printf(m, "trigger_train_fail: %d\n", phytium_dp->trigger_train_fail);
seq_printf(m, "train_retry_count: %d\n", phytium_dp->train_retry_count);
return 0;
}
static int phytium_dp_trigger_train_fail_open(struct inode *inode, struct file *file)
{
return single_open(file, phytium_dp_trigger_train_fail_show, inode->i_private);
}
static const struct file_operations phytium_dp_trigger_train_fail_fops = {
.owner = THIS_MODULE,
.open = phytium_dp_trigger_train_fail_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = phytium_dp_trigger_train_fail_write,
};
static int phytium_edp_backlight_show(struct seq_file *m, void *data)
{
struct drm_connector *connector = m->private;
struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector);
if (!phytium_dp->is_edp)
return -ENODEV;
mutex_lock(&phytium_dp->panel.panel_lock);
seq_printf(m, "backlight: %s\n", phytium_dp->panel.backlight_enabled?"enabled":"disabled");
mutex_unlock(&phytium_dp->panel.panel_lock);
return 0;
}
static int phytium_edp_backlight_open(struct inode *inode, struct file *file)
{
return single_open(file, phytium_edp_backlight_show, inode->i_private);
}
static const struct file_operations phytium_edp_backlight_fops = {
.owner = THIS_MODULE,
.open = phytium_edp_backlight_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int phytium_edp_power_show(struct seq_file *m, void *data)
{
struct drm_connector *connector = m->private;
struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector);
if (!phytium_dp->is_edp)
return -ENODEV;
mutex_lock(&phytium_dp->panel.panel_lock);
seq_printf(m, "power: %s\n", phytium_dp->panel.power_enabled?"enabled":"disabled");
mutex_unlock(&phytium_dp->panel.panel_lock);
return 0;
}
static int phytium_edp_power_open(struct inode *inode, struct file *file)
{
return single_open(file, phytium_edp_power_show, inode->i_private);
}
static const struct file_operations phytium_edp_power_fops = {
.owner = THIS_MODULE,
.open = phytium_edp_power_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
struct dpcd_block {
/* DPCD dump start address. */
unsigned int offset;
/* DPCD dump end address, inclusive. If unset, .size will be used. */
unsigned int end;
/* DPCD dump size. Used if .end is unset. If unset, defaults to 1. */
size_t size;
/* Only valid for eDP. */
bool edp;
};
static const struct dpcd_block phytium_dpcd_debug[] = {
{ .offset = DP_DPCD_REV, .size = DP_RECEIVER_CAP_SIZE },
{ .offset = DP_PSR_SUPPORT, .end = DP_PSR_CAPS },
{ .offset = DP_DOWNSTREAM_PORT_0, .size = 16 },
{ .offset = DP_LINK_BW_SET, .end = DP_EDP_CONFIGURATION_SET },
{ .offset = DP_SINK_COUNT, .end = DP_ADJUST_REQUEST_LANE2_3 },
{ .offset = DP_SET_POWER },
{ .offset = DP_EDP_DPCD_REV },
{ .offset = DP_EDP_GENERAL_CAP_1, .end = DP_EDP_GENERAL_CAP_3 },
{ .offset = DP_EDP_DISPLAY_CONTROL_REGISTER, .end = DP_EDP_BACKLIGHT_FREQ_CAP_MAX_LSB },
{ .offset = DP_EDP_DBC_MINIMUM_BRIGHTNESS_SET, .end = DP_EDP_DBC_MAXIMUM_BRIGHTNESS_SET },
{ .offset = DP_DEVICE_SERVICE_IRQ_VECTOR, .size = 1 },
{ .offset = DP_TEST_REQUEST, .end = DP_TEST_PATTERN },
};
static int phytium_dpcd_show(struct seq_file *m, void *data)
{
struct drm_connector *connector = m->private;
struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector);
uint8_t buf[16], i;
ssize_t err;
if (connector->status != connector_status_connected)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(phytium_dpcd_debug); i++) {
const struct dpcd_block *b = &phytium_dpcd_debug[i];
size_t size = b->end ? b->end - b->offset + 1 : (b->size ?: 1);
if (WARN_ON(size > sizeof(buf)))
continue;
err = drm_dp_dpcd_read(&phytium_dp->aux, b->offset, buf, size);
if (err <= 0) {
DRM_ERROR("dpcd read (%zu bytes at %u) failed (%zd)\n",
size, b->offset, err);
continue;
}
seq_printf(m, "%04x: %*ph\n", b->offset, (int) size, buf);
}
return 0;
}
static int phytium_dpcd_open(struct inode *inode, struct file *file)
{
return single_open(file, phytium_dpcd_show, inode->i_private);
}
static const struct file_operations phytium_dpcd_fops = {
.owner = THIS_MODULE,
.open = phytium_dpcd_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t
phytium_dp_state_write(struct file *filp,
const char __user *ubuf,
size_t len,
loff_t *ppos)
{
char tmp[16];
if (len >= sizeof(tmp))
return -EINVAL;
memset(tmp, 0, sizeof(tmp));
if (copy_from_user(tmp, ubuf, len))
return -EFAULT;
tmp[len] = '\0';
return len;
}
static int phytium_dp_state_show(struct seq_file *m, void *data)
{
struct drm_connector *connector = m->private;
struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector);
seq_printf(m, "port number: %d\n", phytium_dp->port);
seq_printf(m, "source_max_lane_count: %d\n", phytium_dp->source_max_lane_count);
seq_printf(m, "max_source_rates: %d\n",
phytium_dp->source_rates[phytium_dp->num_source_rates-1]);
if (connector->status == connector_status_connected) {
seq_printf(m, "sink_max_lane_count: %d\n", phytium_dp->sink_max_lane_count);
seq_printf(m, "max_sink_rates: %d\n",
phytium_dp->sink_rates[phytium_dp->num_sink_rates-1]);
seq_printf(m, "link_rate: %d\n", phytium_dp->link_rate);
seq_printf(m, "link_lane_count: %d\n", phytium_dp->link_lane_count);
seq_printf(m, "train_set[0]: %d\n", phytium_dp->train_set[0]);
seq_printf(m, "has_audio: %s\n", phytium_dp->has_audio?"yes":"no");
}
return 0;
}
static int phytium_dp_state_open(struct inode *inode, struct file *file)
{
return single_open(file, phytium_dp_state_show, inode->i_private);
}
static const struct file_operations phytium_dp_state_fops = {
.owner = THIS_MODULE,
.open = phytium_dp_state_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = phytium_dp_state_write,
};
static const struct phytium_debugfs_files {
const char *name;
const struct file_operations *fops;
} phytium_debugfs_connector_files[] = {
{"dp_state", &phytium_dp_state_fops},
{"dpcd", &phytium_dpcd_fops},
{"dp_register", &phytium_dp_register_fops},
{"dp_trigger_train_fail", &phytium_dp_trigger_train_fail_fops},
};
static const struct phytium_debugfs_files phytium_edp_debugfs_connector_files[] = {
{"edp_power", &phytium_edp_power_fops},
{"edp_backlight", &phytium_edp_backlight_fops},
};
int phytium_debugfs_connector_add(struct drm_connector *connector)
{
struct dentry *root = connector->debugfs_entry;
struct dentry *ent;
int i;
struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector);
if (!root)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(phytium_debugfs_connector_files); i++) {
ent = debugfs_create_file(phytium_debugfs_connector_files[i].name,
0644,
root,
connector,
phytium_debugfs_connector_files[i].fops);
if (!ent)
return -ENOMEM;
}
if (phytium_dp->is_edp)
for (i = 0; i < ARRAY_SIZE(phytium_edp_debugfs_connector_files); i++) {
ent = debugfs_create_file(phytium_edp_debugfs_connector_files[i].name,
0644,
root,
connector,
phytium_edp_debugfs_connector_files[i].fops);
if (!ent)
return -ENOMEM;
}
return 0;
}
static int phytium_mem_state_show(struct seq_file *m, void *data)
{
struct phytium_display_private *priv = m->private;
uint8_t i;
for (i = 0; i < ARRAY_SIZE(mem_state); i++)
seq_printf(m, "%-34s %10lld\n", mem_state[i], priv->mem_state[i]);
return 0;
}
static int phytium_mem_state_open(struct inode *inode, struct file *file)
{
return single_open(file, phytium_mem_state_show, inode->i_private);
}
static const struct file_operations phytium_mem_state_fops = {
.owner = THIS_MODULE,
.open = phytium_mem_state_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct phytium_debugfs_files phytium_debugfs_display_files[] = {
{"mem_state", &phytium_mem_state_fops},
};
int phytium_debugfs_display_register(struct phytium_display_private *priv)
{
struct drm_minor *minor = priv->dev->primary;
struct dentry *root = minor->debugfs_root;
struct dentry *ent;
if (!root)
return -ENODEV;
ent = debugfs_create_file(phytium_debugfs_display_files[0].name,
0644,
root,
priv,
phytium_debugfs_display_files[0].fops);
if (!ent)
return -ENOMEM;
return 0;
}

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_DEBUGFS_H__
#define __PHYTIUM_DEBUGFS_H__
int phytium_debugfs_connector_add(struct drm_connector *connector);
int phytium_debugfs_display_register(struct phytium_display_private *priv);
#endif /* __PHYTIUM_DEBUGFS_H__ */

View File

@ -0,0 +1,438 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_gem.h>
#include <drm/drm_vblank.h>
#include <drm/drm_irq.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_drv.h>
#include <linux/atomic.h>
#include <drm/drm_atomic.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include "phytium_display_drv.h"
#include "phytium_plane.h"
#include "phytium_crtc.h"
#include "phytium_dp.h"
#include "phytium_gem.h"
#include "phytium_fb.h"
#include "phytium_fbdev.h"
#include "phytium_reg.h"
#include "phytium_pci.h"
#include "phytium_platform.h"
#include "phytium_debugfs.h"
int dc_fake_mode_enable;
module_param(dc_fake_mode_enable, int, 0644);
MODULE_PARM_DESC(dc_fake_mode_enable, "Enable DC fake mode (0-disabled; 1-enabled; default-0)");
int dc_fast_training_check = 1;
module_param(dc_fast_training_check, int, 0644);
MODULE_PARM_DESC(dc_fast_training_check, "Check dp fast training (0-disabled; 1-enabled; default-1)");
int num_source_rates = 4;
module_param(num_source_rates, int, 0644);
MODULE_PARM_DESC(num_source_rates, "set the source max rates (1-1.62Gbps; 2-2.7Gbps; 3-5.4Gbps; 4-8.1Gbps; default-4)");
int source_max_lane_count = 4;
module_param(source_max_lane_count, int, 0644);
MODULE_PARM_DESC(source_max_lane_count, "set the source lane count (1-1lane; 2-2lane; 4-4lane; default-4)");
int link_dynamic_adjust;
module_param(link_dynamic_adjust, int, 0644);
MODULE_PARM_DESC(link_dynamic_adjust, "dynamic select the train pamameter according to the display mode (0-disabled; 1-enabled; default-1)");
int phytium_wait_cmd_done(struct phytium_display_private *priv,
uint32_t register_offset,
uint32_t request_bit,
uint32_t reply_bit)
{
int timeout = 500, config = 0, ret = 0;
do {
mdelay(1);
timeout--;
config = phytium_readl_reg(priv, 0, register_offset);
} while ((!(config & reply_bit)) && timeout);
phytium_writel_reg(priv, config & (~request_bit), 0, register_offset);
if (timeout == 0) {
DRM_ERROR("wait cmd reply timeout\n");
ret = -EBUSY;
} else {
timeout = 500;
do {
mdelay(1);
timeout--;
config = phytium_readl_reg(priv, 0, register_offset);
} while ((config & reply_bit) && timeout);
if (timeout == 0) {
DRM_ERROR("clear cmd timeout\n");
ret = -EBUSY;
}
}
mdelay(5);
return ret;
}
static void phytium_irq_preinstall(struct drm_device *dev)
{
struct phytium_display_private *priv = dev->dev_private;
int i, status;
for_each_pipe_masked(priv, i) {
status = phytium_readl_reg(priv, priv->dc_reg_base[i], PHYTIUM_DC_INT_STATUS);
phytium_writel_reg(priv, INT_DISABLE, priv->dc_reg_base[i], PHYTIUM_DC_INT_ENABLE);
}
}
static void phytium_irq_uninstall(struct drm_device *dev)
{
struct phytium_display_private *priv = dev->dev_private;
int i, status;
for_each_pipe_masked(priv, i) {
status = phytium_readl_reg(priv, priv->dc_reg_base[i], PHYTIUM_DC_INT_STATUS);
phytium_writel_reg(priv, INT_DISABLE, priv->dc_reg_base[i], PHYTIUM_DC_INT_ENABLE);
}
}
static irqreturn_t phytium_display_irq_handler(int irq, void *data)
{
struct drm_device *dev = data;
struct phytium_display_private *priv = dev->dev_private;
bool enabled = 0;
int i = 0, virt_pipe = 0;
irqreturn_t ret = IRQ_NONE, ret1 = IRQ_NONE;
for_each_pipe_masked(priv, i) {
enabled = phytium_readl_reg(priv, priv->dc_reg_base[i], PHYTIUM_DC_INT_STATUS);
if (enabled & INT_STATUS) {
virt_pipe = phytium_get_virt_pipe(priv, i);
if (virt_pipe < 0)
return IRQ_NONE;
drm_handle_vblank(dev, virt_pipe);
ret = IRQ_HANDLED;
if (priv->dc_hw_clear_msi_irq)
priv->dc_hw_clear_msi_irq(priv, i);
}
}
ret1 = phytium_dp_hpd_irq_handler(priv);
if (ret == IRQ_HANDLED || ret1 == IRQ_HANDLED)
return IRQ_HANDLED;
return IRQ_NONE;
}
static const struct drm_mode_config_funcs phytium_mode_funcs = {
.fb_create = phytium_fb_create,
.output_poll_changed = drm_fb_helper_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static void phytium_atomic_commit_tail(struct drm_atomic_state *state)
{
struct drm_device *dev = state->dev;
drm_atomic_helper_commit_modeset_disables(dev, state);
drm_atomic_helper_commit_planes(dev, state, false);
drm_atomic_helper_commit_modeset_enables(dev, state);
drm_atomic_helper_commit_hw_done(state);
drm_atomic_helper_wait_for_flip_done(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
}
static struct drm_mode_config_helper_funcs phytium_mode_config_helpers = {
.atomic_commit_tail = phytium_atomic_commit_tail,
};
static int phytium_modeset_init(struct drm_device *dev)
{
struct phytium_display_private *priv = dev->dev_private;
int i = 0, ret;
drm_mode_config_init(dev);
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
dev->mode_config.max_width = 16384;
dev->mode_config.max_height = 16384;
dev->mode_config.cursor_width = 32;
dev->mode_config.cursor_height = 32;
dev->mode_config.preferred_depth = 24;
dev->mode_config.prefer_shadow = 1;
dev->mode_config.allow_fb_modifiers = true;
dev->mode_config.funcs = &phytium_mode_funcs;
dev->mode_config.helper_private = &phytium_mode_config_helpers;
for_each_pipe_masked(priv, i) {
ret = phytium_crtc_init(dev, i);
if (ret) {
DRM_ERROR("phytium_crtc_init(pipe %d) return failed\n", i);
goto failed_crtc_init;
}
}
for_each_pipe_masked(priv, i) {
ret = phytium_dp_init(dev, i);
if (ret) {
DRM_ERROR("phytium_dp_init(pipe %d) return failed\n", i);
goto failed_dp_init;
}
}
drm_mode_config_reset(dev);
return 0;
failed_dp_init:
failed_crtc_init:
drm_mode_config_cleanup(dev);
return ret;
}
int phytium_get_virt_pipe(struct phytium_display_private *priv, int phys_pipe)
{
int i = 0;
int virt_pipe = 0;
for_each_pipe_masked(priv, i) {
if (i != phys_pipe)
virt_pipe++;
else
return virt_pipe;
}
DRM_ERROR("%s %d failed\n", __func__, phys_pipe);
return -EINVAL;
}
int phytium_get_phys_pipe(struct phytium_display_private *priv, int virt_pipe)
{
int i = 0;
int tmp = 0;
for_each_pipe_masked(priv, i) {
if (tmp != virt_pipe)
tmp++;
else
return i;
}
DRM_ERROR("%s %d failed\n", __func__, virt_pipe);
return -EINVAL;
}
static int phytium_display_load(struct drm_device *dev, unsigned long flags)
{
struct phytium_display_private *priv = dev->dev_private;
int ret = 0;
ret = drm_vblank_init(dev, priv->info.num_pipes);
if (ret) {
DRM_ERROR("vblank init failed\n");
goto failed_vblank_init;
}
ret = phytium_modeset_init(dev);
if (ret) {
DRM_ERROR("phytium_modeset_init failed\n");
goto failed_modeset_init;
}
if (priv->support_memory_type & MEMORY_TYPE_VRAM)
priv->vram_hw_init(priv);
ret = drm_irq_install(dev, priv->irq);
if (ret) {
DRM_ERROR("install irq failed\n");
goto failed_irq_install;
}
ret = phytium_drm_fbdev_init(dev);
if (ret)
DRM_ERROR("failed to init dev\n");
phytium_debugfs_display_register(priv);
return ret;
failed_irq_install:
drm_mode_config_cleanup(dev);
failed_modeset_init:
failed_vblank_init:
return ret;
}
static void phytium_display_unload(struct drm_device *dev)
{
phytium_drm_fbdev_fini(dev);
drm_irq_uninstall(dev);
drm_mode_config_cleanup(dev);
}
static const struct drm_ioctl_desc phytium_ioctls[] = {
/* for test, none so far */
};
static const struct file_operations phytium_drm_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
.compat_ioctl = drm_compat_ioctl,
.poll = drm_poll,
.read = drm_read,
.llseek = no_llseek,
.mmap = phytium_gem_mmap,
};
struct drm_driver phytium_display_drm_driver = {
.driver_features = DRIVER_HAVE_IRQ |
DRIVER_MODESET |
DRIVER_ATOMIC |
DRIVER_GEM,
.load = phytium_display_load,
.unload = phytium_display_unload,
.lastclose = drm_fb_helper_lastclose,
.irq_handler = phytium_display_irq_handler,
.irq_preinstall = phytium_irq_preinstall,
.irq_uninstall = phytium_irq_uninstall,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_export = drm_gem_prime_export,
.gem_prime_import = drm_gem_prime_import,
.gem_prime_import_sg_table = phytium_gem_prime_import_sg_table,
.gem_prime_mmap = phytium_gem_prime_mmap,
.dumb_create = phytium_gem_dumb_create,
.dumb_destroy = phytium_gem_dumb_destroy,
.ioctls = phytium_ioctls,
.num_ioctls = ARRAY_SIZE(phytium_ioctls),
.fops = &phytium_drm_driver_fops,
.name = DRV_NAME,
.desc = DRV_DESC,
.date = DRV_DATE,
.major = DRV_MAJOR,
.minor = DRV_MINOR,
};
static void phytium_display_shutdown(struct drm_device *dev)
{
drm_atomic_helper_shutdown(dev);
}
static int phytium_display_pm_suspend(struct drm_device *dev)
{
struct drm_atomic_state *state;
struct phytium_display_private *priv = dev->dev_private;
int ret, ret1;
phytium_dp_hpd_irq_setup(dev, false);
cancel_work_sync(&priv->hotplug_work);
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 1);
state = drm_atomic_helper_suspend(dev);
if (IS_ERR(state)) {
DRM_ERROR("drm_atomic_helper_suspend failed: %ld\n", PTR_ERR(state));
ret = PTR_ERR(state);
goto suspend_failed;
}
dev->mode_config.suspend_state = state;
ret = phytium_gem_suspend(dev);
if (ret) {
DRM_ERROR("phytium_gem_suspend failed: %d\n", ret);
goto gem_suspend_failed;
}
return 0;
gem_suspend_failed:
ret1 = drm_atomic_helper_resume(dev, dev->mode_config.suspend_state);
if (ret1)
DRM_ERROR("Failed to resume (%d)\n", ret1);
dev->mode_config.suspend_state = NULL;
suspend_failed:
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0);
phytium_dp_hpd_irq_setup(dev, true);
return ret;
}
static int phytium_display_pm_resume(struct drm_device *dev)
{
struct phytium_display_private *priv = dev->dev_private;
int ret = 0;
if (WARN_ON(!dev->mode_config.suspend_state))
return -EINVAL;
ret = phytium_dp_resume(dev);
if (ret)
return -EIO;
phytium_crtc_resume(dev);
phytium_gem_resume(dev);
if (priv->support_memory_type & MEMORY_TYPE_VRAM)
priv->vram_hw_init(priv);
ret = drm_atomic_helper_resume(dev, dev->mode_config.suspend_state);
if (ret) {
DRM_ERROR("Failed to resume (%d)\n", ret);
return ret;
}
dev->mode_config.suspend_state = NULL;
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0);
phytium_dp_hpd_irq_setup(dev, true);
return 0;
}
void phytium_display_private_init(struct phytium_display_private *priv, struct drm_device *dev)
{
INIT_LIST_HEAD(&priv->gem_list_head);
spin_lock_init(&priv->hotplug_irq_lock);
INIT_WORK(&priv->hotplug_work, phytium_dp_hpd_work_func);
memset(priv->mem_state, 0, sizeof(priv->mem_state));
priv->dev = dev;
priv->display_shutdown = phytium_display_shutdown;
priv->display_pm_suspend = phytium_display_pm_suspend;
priv->display_pm_resume = phytium_display_pm_resume;
}
static int __init phytium_display_init(void)
{
int ret = 0;
ret = platform_driver_register(&phytium_platform_driver);
if (ret)
return ret;
ret = pci_register_driver(&phytium_pci_driver);
return ret;
}
static void __exit phytium_display_exit(void)
{
pci_unregister_driver(&phytium_pci_driver);
platform_driver_unregister(&phytium_platform_driver);
}
module_init(phytium_display_init);
module_exit(phytium_display_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yang Xun <yangxun@phytium.com.cn>");
MODULE_DESCRIPTION("Phytium Display Controller");

View File

@ -0,0 +1,174 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_DISPLAY_DRV_H__
#define __PHYTIUM_DISPLAY_DRV_H__
#include <drm/drm_print.h>
#include <drm/drm_fb_helper.h>
#define DEBUG_LOG 0
#define PHYTIUM_FORMAT_MAX_PLANE 3
#define DP_MAX_DOWNSTREAM_PORTS 0x10
#define DRV_NAME "dc"
#define DRV_DESC "phytium dc"
#define DRV_DATE "20201220"
#define DRV_MAJOR 1
#define DRV_MINOR 1
/* come from GPU */
#define DRM_FORMAT_MOD_VENDOR_PHYTIUM 0x92
/* dc:mode0 8x8 16bpp gpu: FBCDC_8X8_V10 */
#define DRM_FORMAT_MOD_PHYTIUM_TILE_MODE0_FBCDC fourcc_mod_code(PHYTIUM, 21)
/* dc:mode3 8x4 32bpp gpu: FBCDC_16X4_v10 */
#define DRM_FORMAT_MOD_PHYTIUM_TILE_MODE3_FBCDC fourcc_mod_code(PHYTIUM, 22)
#define PIPE_MASK_SHIFT 0x0
#define PIPE_MASK_MASK 0x7
#define EDP_MASK_SHIFT 0x3
#define EDP_MASK_MASK 0x7
enum phytium_platform {
PHYTIUM_PLATFORM_UNINITIALIZED = 0,
PHYTIUM_PLATFORM_PX210,
PHYTIUM_PLATFORM_PE220X,
};
enum phytium_mem_state_type {
PHYTIUM_MEM_VRAM_TOTAL = 0,
PHYTIUM_MEM_VRAM_ALLOC,
PHYTIUM_MEM_SYSTEM_CARVEOUT_TOTAL,
PHYTIUM_MEM_SYSTEM_CARVEOUT_ALLOC,
PHYTIUM_MEM_SYSTEM_UNIFIED_ALLOC,
PHYTIUM_MEM_STATE_TYPE_COUNT,
};
#define MEMORY_TYPE_VRAM 0x1
#define MEMORY_TYPE_SYSTEM_CARVEOUT 0x2
#define MEMORY_TYPE_SYSTEM_UNIFIED 0x4
#define IS_PLATFORM(priv, p) ((priv)->info.platform_mask & BIT(p))
#define IS_PX210(priv) IS_PLATFORM(priv, PHYTIUM_PLATFORM_PX210)
#define IS_PE220X(priv) IS_PLATFORM(priv, PHYTIUM_PLATFORM_PE220X)
struct phytium_device_info {
unsigned char platform_mask;
unsigned char pipe_mask;
unsigned char num_pipes;
unsigned char total_pipes;
unsigned char edp_mask;
unsigned int crtc_clock_max;
unsigned int hdisplay_max;
unsigned int vdisplay_max;
unsigned int backlight_max;
unsigned long address_mask;
};
struct phytium_display_private {
/* hw */
void __iomem *regs;
void __iomem *vram_addr;
struct phytium_device_info info;
char support_memory_type;
char reserve[3];
uint32_t dc_reg_base[3];
uint32_t dcreq_reg_base[3];
uint32_t dp_reg_base[3];
uint32_t address_transform_base;
uint32_t phy_access_base[3];
/* drm */
struct drm_device *dev;
int irq;
/* fb_dev */
struct drm_fb_helper fbdev_helper;
struct phytium_gem_object *fbdev_phytium_gem;
int save_reg[3];
struct list_head gem_list_head;
struct work_struct hotplug_work;
spinlock_t hotplug_irq_lock;
void (*vram_hw_init)(struct phytium_display_private *priv);
void (*display_shutdown)(struct drm_device *dev);
int (*display_pm_suspend)(struct drm_device *dev);
int (*display_pm_resume)(struct drm_device *dev);
void (*dc_hw_clear_msi_irq)(struct phytium_display_private *priv, uint32_t phys_pipe);
int (*dc_hw_fb_format_check)(const struct drm_mode_fb_cmd2 *mode_cmd, int count);
struct gen_pool *memory_pool;
resource_size_t pool_phys_addr;
resource_size_t pool_size;
void *pool_virt_addr;
uint64_t mem_state[PHYTIUM_MEM_STATE_TYPE_COUNT];
/* DMA info */
int dma_inited;
struct dma_chan *dma_chan;
};
static inline unsigned int
phytium_readl_reg(struct phytium_display_private *priv, uint32_t group_offset, uint32_t reg_offset)
{
unsigned int data;
data = readl(priv->regs + group_offset + reg_offset);
#if DEBUG_LOG
pr_info("Read 32'h%08x 32'h%08x\n", group_offset + reg_offset, data);
#endif
return data;
}
static inline void
phytium_writel_reg(struct phytium_display_private *priv, uint32_t data,
uint32_t group_offset, uint32_t reg_offset)
{
writel(data, priv->regs + group_offset + reg_offset);
#if DEBUG_LOG
pr_info("Write 32'h%08x 32'h%08x\n", group_offset + reg_offset, data);
#endif
}
static inline void
phytium_writeb_reg(struct phytium_display_private *priv, uint8_t data,
uint32_t group_offset, uint32_t reg_offset)
{
writeb(data, priv->regs + group_offset + reg_offset);
#if DEBUG_LOG
pr_info("Write 32'h%08x 8'h%08x\n", group_offset + reg_offset, data);
#endif
}
#define for_each_pipe(__dev_priv, __p) \
for ((__p) = 0; (__p) < __dev_priv->info.total_pipes; (__p)++)
#define for_each_pipe_masked(__dev_priv, __p) \
for ((__p) = 0; (__p) < __dev_priv->info.total_pipes; (__p)++) \
for_each_if((__dev_priv->info.pipe_mask) & BIT(__p))
int phytium_get_virt_pipe(struct phytium_display_private *priv, int phys_pipe);
int phytium_get_phys_pipe(struct phytium_display_private *priv, int virt_pipe);
int phytium_wait_cmd_done(struct phytium_display_private *priv,
uint32_t register_offset,
uint32_t request_bit,
uint32_t reply_bit);
void phytium_display_private_init(struct phytium_display_private *priv, struct drm_device *dev);
extern struct drm_driver phytium_display_drm_driver;
extern int dc_fake_mode_enable;
extern int dc_fast_training_check;
extern int num_source_rates;
extern int source_max_lane_count;
extern int link_dynamic_adjust;
#endif /* __PHYTIUM_DISPLAY_DRV_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,154 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_DP_H__
#define __PHYTIUM_DP_H__
#include <drm/drm_dp_helper.h>
#include <drm/drm_encoder.h>
#include <sound/hdmi-codec.h>
struct phytium_dp_device;
#include "phytium_panel.h"
struct audio_info {
int sample_rate;
int channels;
int sample_width;
};
struct dp_audio_n_m {
int sample_rate;
int link_rate;
u16 m;
u16 n;
};
struct phytium_dp_compliance {
unsigned long test_type;
uint32_t test_link_rate;
u8 test_lane_count;
bool test_active;
u8 reserve[2];
};
struct phytium_dp_func {
uint8_t (*dp_hw_get_source_lane_count)(struct phytium_dp_device *phytium_dp);
int (*dp_hw_reset)(struct phytium_dp_device *phytium_dp);
bool (*dp_hw_spread_is_enable)(struct phytium_dp_device *phytium_dp);
int (*dp_hw_set_backlight)(struct phytium_dp_device *phytium_dp, uint32_t level);
uint32_t (*dp_hw_get_backlight)(struct phytium_dp_device *phytium_dp);
void (*dp_hw_disable_backlight)(struct phytium_dp_device *phytium_dp);
void (*dp_hw_enable_backlight)(struct phytium_dp_device *phytium_dp);
void (*dp_hw_poweroff_panel)(struct phytium_dp_device *phytium_dp);
void (*dp_hw_poweron_panel)(struct phytium_dp_device *phytium_dp);
int (*dp_hw_init_phy)(struct phytium_dp_device *phytium_dp);
void (*dp_hw_set_phy_lane_setting)(struct phytium_dp_device *phytium_dp,
uint32_t link_rate, uint8_t train_set);
int (*dp_hw_set_phy_lane_and_rate)(struct phytium_dp_device *phytium_dp,
uint8_t link_lane_count,
uint32_t link_rate);
};
struct phytium_dp_hpd_state {
bool hpd_event_state;
bool hpd_irq_state;
bool hpd_raw_state;
bool hpd_irq_enable;
};
struct phytium_dp_device {
struct drm_device *dev;
struct drm_encoder encoder;
struct drm_connector connector;
int port;
struct drm_display_mode mode;
bool link_trained;
bool detect_done;
bool is_edp;
bool reserve0;
struct drm_dp_aux aux;
unsigned char dpcd[DP_RECEIVER_CAP_SIZE];
uint8_t edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
unsigned char downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
unsigned char sink_count;
int *source_rates;
int num_source_rates;
int sink_rates[DP_MAX_SUPPORTED_RATES];
int num_sink_rates;
int common_rates[DP_MAX_SUPPORTED_RATES];
int num_common_rates;
int source_max_lane_count;
int sink_max_lane_count;
int common_max_lane_count;
int max_link_rate;
int max_link_lane_count;
int link_rate;
int link_lane_count;
struct work_struct train_retry_work;
int train_retry_count;
uint32_t trigger_train_fail;
unsigned char train_set[4];
struct edid *edp_edid;
bool has_audio;
bool fast_train_support;
bool hw_spread_enable;
bool reserve[1];
struct platform_device *audio_pdev;
struct audio_info audio_info;
hdmi_codec_plugged_cb plugged_cb;
struct device *codec_dev;
struct phytium_dp_compliance compliance;
struct phytium_dp_func *funcs;
struct phytium_dp_hpd_state dp_hpd_state;
struct phytium_panel panel;
struct drm_display_mode native_mode;
};
union phytium_phy_tp {
struct {
/* DpcdPhyTestPatterns. This field is 2 bits for DP1.1
* and 3 bits for DP1.2.
*/
uint8_t PATTERN :3;
uint8_t RESERVED :5;
} bits;
uint8_t raw;
};
/* PHY test patterns
* The order of test patterns follows DPCD register PHY_TEST_PATTERN (0x248)
*/
enum phytium_dpcd_phy_tp {
PHYTIUM_PHY_TP_NONE = 0,
PHYTIUM_PHY_TP_D10_2,
PHYTIUM_PHY_TP_SYMBOL_ERROR,
PHYTIUM_PHY_TP_PRBS7,
PHYTIUM_PHY_TP_80BIT_CUSTOM,
PHYTIUM_PHY_TP_CP2520_1,
PHYTIUM_PHY_TP_CP2520_2,
PHYTIUM_PHY_TP_CP2520_3,
};
#define encoder_to_dp_device(x) container_of(x, struct phytium_dp_device, encoder)
#define connector_to_dp_device(x) container_of(x, struct phytium_dp_device, connector)
#define panel_to_dp_device(x) container_of(x, struct phytium_dp_device, panel)
#define train_retry_to_dp_device(x) container_of(x, struct phytium_dp_device, train_retry_work)
void phytium_phy_writel(struct phytium_dp_device *phytium_dp, uint32_t address, uint32_t data);
uint32_t phytium_phy_readl(struct phytium_dp_device *phytium_dp, uint32_t address);
int phytium_dp_init(struct drm_device *dev, int pipe);
int phytium_dp_resume(struct drm_device *drm_dev);
void phytium_dp_hpd_irq_setup(struct drm_device *dev, bool enable);
irqreturn_t phytium_dp_hpd_irq_handler(struct phytium_display_private *priv);
void phytium_dp_hpd_work_func(struct work_struct *work);
const struct dp_audio_n_m *phytium_dp_audio_get_n_m(int link_rate, int sample_rate);
#endif /* __PHYTIUM_DP_H__ */

View File

@ -0,0 +1,131 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <drm/drm_gem.h>
#include <drm/drm_modeset_helper.h>
#include <drm/drm_fourcc.h>
#include "phytium_display_drv.h"
#include "phytium_fb.h"
#include "phytium_gem.h"
static int
phytium_fb_create_handle(struct drm_framebuffer *fb, struct drm_file *file_priv,
unsigned int *handle)
{
struct phytium_framebuffer *phytium_fb = to_phytium_framebuffer(fb);
return drm_gem_handle_create(file_priv, &phytium_fb->phytium_gem_obj[0]->base, handle);
}
static void phytium_fb_destroy(struct drm_framebuffer *fb)
{
struct phytium_framebuffer *phytium_fb = to_phytium_framebuffer(fb);
int i, num_planes;
struct drm_gem_object *obj = NULL;
const struct drm_format_info *info;
info = drm_format_info(fb->format->format);
num_planes = info ? info->num_planes : 1;
for (i = 0; i < num_planes; i++) {
obj = &phytium_fb->phytium_gem_obj[i]->base;
if (obj)
drm_gem_object_put(obj);
}
drm_framebuffer_cleanup(fb);
kfree(phytium_fb);
}
static struct drm_framebuffer_funcs viv_fb_funcs = {
.create_handle = phytium_fb_create_handle,
.destroy = phytium_fb_destroy,
};
struct phytium_framebuffer *
phytium_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
struct phytium_gem_object **phytium_gem_obj, unsigned int num_planes)
{
struct phytium_framebuffer *phytium_fb;
int ret = 0, i;
phytium_fb = kzalloc(sizeof(*phytium_fb), GFP_KERNEL);
if (!phytium_fb)
return ERR_PTR(-ENOMEM);
drm_helper_mode_fill_fb_struct(dev, &phytium_fb->base, mode_cmd);
ret = drm_framebuffer_init(dev, &phytium_fb->base, &viv_fb_funcs);
if (ret) {
DRM_ERROR("Failed to initialize framebuffer: %d\n", ret);
kfree(phytium_fb);
return ERR_PTR(ret);
}
for (i = 0; i < num_planes; i++) {
phytium_fb->phytium_gem_obj[i] = phytium_gem_obj[i];
phytium_fb->base.obj[i] = &phytium_gem_obj[i]->base;
}
return phytium_fb;
}
struct drm_framebuffer *
phytium_fb_create(struct drm_device *dev, struct drm_file *file_priv,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
int ret = 0, i, num_planes;
struct drm_gem_object *obj;
unsigned int hsub, vsub, size;
struct phytium_gem_object *phytium_gem_obj[PHYTIUM_FORMAT_MAX_PLANE] = {0};
struct phytium_framebuffer *phytium_fb;
struct phytium_display_private *priv = dev->dev_private;
const struct drm_format_info *info;
info = drm_format_info(mode_cmd->pixel_format);
hsub = info ? info->hsub : 1;
vsub = info ? info->vsub : 1;
num_planes = info ? info->num_planes : 1;
num_planes = min(num_planes, PHYTIUM_FORMAT_MAX_PLANE);
for (i = 0; i < num_planes; i++) {
unsigned int height = mode_cmd->height / (i ? vsub : 1);
size = height * mode_cmd->pitches[i] + mode_cmd->offsets[i];
obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
if (!obj) {
DRM_ERROR("Failed to lookup GEM object\n");
ret = -ENXIO;
goto error;
}
if (obj->size < size) {
drm_gem_object_put(obj);
ret = -EINVAL;
goto error;
}
phytium_gem_obj[i] = to_phytium_gem_obj(obj);
ret = priv->dc_hw_fb_format_check(mode_cmd, i);
if (ret < 0)
goto error;
}
phytium_fb = phytium_fb_alloc(dev, mode_cmd, phytium_gem_obj, i);
if (IS_ERR(phytium_fb)) {
DRM_DEBUG_KMS("phytium_fb_alloc failed\n");
ret = PTR_ERR(phytium_fb);
goto error;
}
return &phytium_fb->base;
error:
for (i--; i >= 0; i--)
drm_gem_object_put(&phytium_gem_obj[i]->base);
return ERR_PTR(ret);
}

View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_FB_H__
#define __PHYTIUM_FB_H__
#include <drm/drm_framebuffer.h>
struct phytium_framebuffer {
struct drm_framebuffer base;
struct phytium_gem_object *phytium_gem_obj[PHYTIUM_FORMAT_MAX_PLANE];
};
#define to_phytium_framebuffer(fb) container_of(fb, struct phytium_framebuffer, base)
struct phytium_framebuffer *phytium_fb_alloc(struct drm_device *dev,
const struct drm_mode_fb_cmd2 *mode_cmd,
struct phytium_gem_object **phytium_gem_obj,
unsigned int num_planes);
struct drm_framebuffer *phytium_fb_create(struct drm_device *dev, struct drm_file *file_priv,
const struct drm_mode_fb_cmd2 *mode_cmd);
#endif /* __PHYTIUM_FB_H__ */

View File

@ -0,0 +1,146 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <drm/drm.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fourcc.h>
#include "phytium_display_drv.h"
#include "phytium_gem.h"
#include "phytium_fb.h"
#define PHYTIUM_MAX_CONNECTOR 1
#define helper_to_drm_private(x) container_of(x, struct phytium_display_private, fbdev_helper)
static int phytium_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
struct drm_fb_helper *helper = info->par;
struct phytium_display_private *priv = helper_to_drm_private(helper);
return phytium_gem_mmap_obj(&priv->fbdev_phytium_gem->base, vma);
}
static struct fb_ops phytium_fbdev_ops = {
.owner = THIS_MODULE,
DRM_FB_HELPER_DEFAULT_OPS,
.fb_mmap = phytium_fbdev_mmap,
.fb_fillrect = drm_fb_helper_cfb_fillrect,
.fb_copyarea = drm_fb_helper_cfb_copyarea,
.fb_imageblit = drm_fb_helper_cfb_imageblit,
};
static int
phytium_drm_fbdev_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes)
{
struct phytium_display_private *priv = helper_to_drm_private(helper);
struct drm_device *dev = helper->dev;
unsigned int bytes_per_pixel;
struct drm_mode_fb_cmd2 mode_cmd = {0};
struct phytium_framebuffer *phytium_fb = NULL;
struct fb_info *fbi = NULL;
struct drm_framebuffer *fb = NULL;
size_t size = 0;
int ret = 0;
unsigned long offset;
bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
mode_cmd.pitches[0] = ALIGN(sizes->surface_width * bytes_per_pixel, 128);
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
size = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height);
ret = mutex_lock_interruptible(&dev->struct_mutex);
if (ret < 0) {
DRM_ERROR("failed to get mutex lock\n");
return ret;
}
priv->fbdev_phytium_gem = phytium_gem_create_object(dev, size);
if (!priv->fbdev_phytium_gem) {
DRM_ERROR("failed to create gem object\n");
return -ENOMEM;
}
mutex_unlock(&dev->struct_mutex);
fbi = drm_fb_helper_alloc_fbi(helper);
if (IS_ERR(fbi)) {
DRM_DEV_ERROR(dev->dev, "Failed to create framebuffer info.");
ret = PTR_ERR(fbi);
goto out;
}
phytium_fb = phytium_fb_alloc(dev, &mode_cmd, &priv->fbdev_phytium_gem, 1);
if (IS_ERR(phytium_fb)) {
DRM_DEV_ERROR(dev->dev, "Failed to alloc DRM framebuffer.\n");
ret = PTR_ERR(phytium_fb);
goto out;
}
helper->fb = &(phytium_fb->base);
fbi->par = helper;
fbi->flags = FBINFO_FLAG_DEFAULT;
fbi->fbops = &phytium_fbdev_ops;
fb = helper->fb;
drm_fb_helper_fill_info(fbi, helper, sizes);
offset = fbi->var.xoffset * bytes_per_pixel;
offset += fbi->var.yoffset * fb->pitches[0];
dev->mode_config.fb_base = 0;
fbi->screen_base = priv->fbdev_phytium_gem->vaddr + offset;
fbi->screen_size = priv->fbdev_phytium_gem->base.size;
fbi->fix.smem_len = priv->fbdev_phytium_gem->base.size;
DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%pa offset=%ld size=%zu\n", fb->width, fb->height,
fb->format->depth, &priv->fbdev_phytium_gem->iova, offset, size);
fbi->skip_vt_switch = true;
return 0;
out:
phytium_gem_free_object(&priv->fbdev_phytium_gem->base);
return ret;
}
static const struct drm_fb_helper_funcs phytium_drm_fb_helper_funcs = {
.fb_probe = phytium_drm_fbdev_create,
};
int phytium_drm_fbdev_init(struct drm_device *dev)
{
struct phytium_display_private *priv = dev->dev_private;
struct drm_fb_helper *helper;
int ret;
if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
return -EINVAL;
helper = &priv->fbdev_helper;
drm_fb_helper_prepare(dev, helper, &phytium_drm_fb_helper_funcs);
ret = drm_fb_helper_init(dev, helper,PHYTIUM_MAX_CONNECTOR);
if (ret < 0) {
DRM_DEV_ERROR(dev->dev, "Failed to initialize drm fb helper -ret %d\n", ret);
return ret;
}
ret = drm_fb_helper_initial_config(helper, 32);
return 0;
}
void phytium_drm_fbdev_fini(struct drm_device *dev)
{
struct phytium_display_private *priv = dev->dev_private;
struct drm_fb_helper *helper;
helper = &priv->fbdev_helper;
drm_fb_helper_unregister_fbi(helper);
if (helper->fb)
drm_framebuffer_put(helper->fb);
drm_fb_helper_fini(helper);
}

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef _PHYTIUM_FBDEV_H
#define _PHYTIUM_FBDEV_H
int phytium_drm_fbdev_init(struct drm_device *dev);
void phytium_drm_fbdev_fini(struct drm_device *dev);
#endif /* _PHYTIUM_FBDEV_H */

View File

@ -0,0 +1,522 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <linux/dma-buf.h>
#include <linux/vmalloc.h>
#include <linux/mm_types.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/genalloc.h>
#include <drm/drm_prime.h>
#include <linux/dmaengine.h>
#include <linux/completion.h>
#include "phytium_display_drv.h"
#include "phytium_gem.h"
#define VRAM_POOL_ALLOC_ORDER 12
int phytium_memory_pool_alloc(struct phytium_display_private *priv, void **pvaddr,
phys_addr_t *phys_addr, uint64_t size)
{
unsigned long vaddr;
vaddr = gen_pool_alloc(priv->memory_pool, size);
if (!vaddr)
return -ENOMEM;
*phys_addr = gen_pool_virt_to_phys(priv->memory_pool, vaddr);
*pvaddr = (void *)vaddr;
return 0;
}
void phytium_memory_pool_free(struct phytium_display_private *priv, void *vaddr, uint64_t size)
{
gen_pool_free(priv->memory_pool, (unsigned long)vaddr, size);
}
int phytium_memory_pool_init(struct device *dev, struct phytium_display_private *priv)
{
int ret = 0;
priv->memory_pool = gen_pool_create(VRAM_POOL_ALLOC_ORDER, -1);
if (priv->memory_pool == NULL) {
DRM_ERROR("fail to create memory pool\n");
ret = -1;
goto failed_create_pool;
}
ret = gen_pool_add_virt(priv->memory_pool, (unsigned long)priv->pool_virt_addr,
priv->pool_phys_addr, priv->pool_size, -1);
if (ret) {
DRM_ERROR("fail to add vram pool\n");
ret = -1;
goto failed_add_pool_virt;
}
return 0;
failed_add_pool_virt:
gen_pool_destroy(priv->memory_pool);
failed_create_pool:
return ret;
}
void phytium_memory_pool_fini(struct device *dev, struct phytium_display_private *priv)
{
gen_pool_destroy(priv->memory_pool);
}
struct sg_table *
phytium_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
struct phytium_gem_object *phytium_gem_obj = to_phytium_gem_obj(obj);
struct sg_table *sgt;
struct drm_device *dev = obj->dev;
int ret;
struct page *page = NULL;
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt) {
DRM_DEBUG_KMS("malloc sgt fail\n");
return ERR_PTR(-ENOMEM);
}
if ((phytium_gem_obj->memory_type == MEMORY_TYPE_VRAM) ||
(phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_CARVEOUT)) {
ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
if (ret) {
DRM_ERROR("failed to allocate sg\n");
goto sgt_free;
}
page = phys_to_page(phytium_gem_obj->phys_addr);
sg_set_page(sgt->sgl, page, PAGE_ALIGN(phytium_gem_obj->size), 0);
} else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_UNIFIED) {
ret = dma_get_sgtable_attrs(dev->dev, sgt, phytium_gem_obj->vaddr,
phytium_gem_obj->iova, phytium_gem_obj->size,
DMA_ATTR_WRITE_COMBINE);
if (ret) {
DRM_ERROR("failed to allocate sgt, %d\n", ret);
goto sgt_free;
}
}
return sgt;
sgt_free:
kfree(sgt);
return ERR_PTR(ret);
}
struct drm_gem_object *
phytium_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt)
{
struct phytium_gem_object *phytium_gem_obj = NULL;
struct scatterlist *s;
dma_addr_t expected;
int ret, i;
phytium_gem_obj = kzalloc(sizeof(*phytium_gem_obj), GFP_KERNEL);
if (!phytium_gem_obj) {
DRM_ERROR("failed to allocate phytium_gem_obj\n");
ret = -ENOMEM;
goto failed_malloc;
}
ret = drm_gem_object_init(dev, &phytium_gem_obj->base, attach->dmabuf->size);
if (ret) {
DRM_ERROR("failed to initialize drm gem object: %d\n", ret);
goto failed_object_init;
}
expected = sg_dma_address(sgt->sgl);
for_each_sg(sgt->sgl, s, sgt->nents, i) {
if (sg_dma_address(s) != expected) {
DRM_ERROR("sg_table is not contiguous");
ret = -EINVAL;
goto failed_check_continue;
}
expected = sg_dma_address(s) + sg_dma_len(s);
}
phytium_gem_obj->iova = sg_dma_address(sgt->sgl);
phytium_gem_obj->sgt = sgt;
return &phytium_gem_obj->base;
failed_check_continue:
drm_gem_object_release(&phytium_gem_obj->base);
failed_object_init:
kfree(phytium_gem_obj);
failed_malloc:
return ERR_PTR(ret);
}
void *phytium_gem_prime_vmap(struct drm_gem_object *obj)
{
struct phytium_gem_object *phytium_obj = to_phytium_gem_obj(obj);
return phytium_obj->vaddr;
}
void phytium_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
{
return;
}
int phytium_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
{
int ret = 0;
ret = drm_gem_mmap_obj(obj, obj->size, vma);
if (ret < 0)
return ret;
return phytium_gem_mmap_obj(obj, vma);
}
static void phytium_dma_callback(void *callback_param)
{
struct completion *comp = callback_param;
complete(comp);
}
int phytium_dma_transfer(struct drm_device *drm_dev, int dev_to_mem, void *addr,
dma_addr_t iova, uint64_t size)
{
struct phytium_display_private *priv = drm_dev->dev_private;
struct dma_chan *dma_chan = priv->dma_chan;
struct sg_table st;
struct scatterlist *sgl;
int ret = 0, timeout;
uint32_t nents, i;
struct dma_slave_config cfg = {0};
struct dma_async_tx_descriptor *desc;
struct completion comp;
enum dma_data_direction dir;
size_t min = 0;
nents = DIV_ROUND_UP(size, PAGE_SIZE);
ret = sg_alloc_table(&st, nents, GFP_KERNEL);
if (ret) {
DRM_ERROR("failed to allocate sg_table\n");
ret = -ENOMEM;
goto failed_sg_alloc_table;
}
for_each_sg(st.sgl, sgl, st.nents, i) {
min = min_t(size_t, size, PAGE_SIZE - offset_in_page(addr));
sg_set_page(sgl, vmalloc_to_page(addr), min, offset_in_page(addr));
addr += min;
size -= min;
}
memset(&cfg, 0, sizeof(cfg));
if (dev_to_mem) {
cfg.direction = DMA_DEV_TO_MEM;
cfg.src_addr = iova;
cfg.dst_addr = 0;
dir = DMA_FROM_DEVICE;
} else {
cfg.direction = DMA_MEM_TO_DEV;
cfg.src_addr = 0;
cfg.dst_addr = iova;
dir = DMA_TO_DEVICE;
}
dmaengine_slave_config(dma_chan, &cfg);
nents = dma_map_sg(dma_chan->device->dev, st.sgl, st.nents, dir);
if (!nents) {
DRM_DEV_ERROR(drm_dev->dev, "failed to dma_map_sg for dmaengine\n");
ret = -EINVAL;
goto failed_dma_map_sg;
}
st.nents = nents;
dma_sync_sg_for_device(dma_chan->device->dev, st.sgl, st.nents, dir);
sgl = st.sgl;
desc = dmaengine_prep_slave_sg(dma_chan,
st.sgl,
st.nents,
cfg.direction,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
DRM_DEV_ERROR(drm_dev->dev, "failed to dmaengine_prep_slave_sg\n");
ret = -EINVAL;
goto failed_prep_slave_sg;
}
init_completion(&comp);
desc->callback = phytium_dma_callback;
desc->callback_param = &comp;
dmaengine_submit(desc);
dma_async_issue_pending(dma_chan);
timeout = wait_for_completion_timeout(&comp, 2 * HZ);
if (timeout == 0) {
DRM_DEV_ERROR(drm_dev->dev, "wait for dma callback timeout\n");
ret = -EIO;
}
dma_sync_sg_for_cpu(dma_chan->device->dev, st.sgl, st.nents, dir);
failed_prep_slave_sg:
dma_unmap_sg(dma_chan->device->dev, st.sgl, st.nents, dir);
failed_dma_map_sg:
sg_free_table(&st);
failed_sg_alloc_table:
return ret;
}
int phytium_gem_suspend(struct drm_device *drm_dev)
{
struct phytium_display_private *priv = drm_dev->dev_private;
struct phytium_gem_object *phytium_gem_obj = NULL;
int ret = 0;
list_for_each_entry(phytium_gem_obj, &priv->gem_list_head, list) {
if (phytium_gem_obj->memory_type != MEMORY_TYPE_VRAM)
continue;
phytium_gem_obj->vaddr_save = vmalloc(phytium_gem_obj->size);
if (!phytium_gem_obj->vaddr_save)
goto malloc_failed;
if (priv->dma_inited)
ret = phytium_dma_transfer(drm_dev, 1, phytium_gem_obj->vaddr_save,
phytium_gem_obj->iova, phytium_gem_obj->size);
if ((!priv->dma_inited) || ret)
memcpy(phytium_gem_obj->vaddr_save, phytium_gem_obj->vaddr,
phytium_gem_obj->size);
}
return 0;
malloc_failed:
list_for_each_entry(phytium_gem_obj, &priv->gem_list_head, list) {
if (phytium_gem_obj->memory_type != MEMORY_TYPE_VRAM)
continue;
if (phytium_gem_obj->vaddr_save) {
vfree(phytium_gem_obj->vaddr_save);
phytium_gem_obj->vaddr_save = NULL;
}
}
return -ENOMEM;
}
void phytium_gem_resume(struct drm_device *drm_dev)
{
struct phytium_display_private *priv = drm_dev->dev_private;
struct phytium_gem_object *phytium_gem_obj = NULL;
list_for_each_entry(phytium_gem_obj, &priv->gem_list_head, list) {
if (phytium_gem_obj->memory_type != MEMORY_TYPE_VRAM)
continue;
memcpy(phytium_gem_obj->vaddr, phytium_gem_obj->vaddr_save, phytium_gem_obj->size);
vfree(phytium_gem_obj->vaddr_save);
phytium_gem_obj->vaddr_save = NULL;
}
}
void phytium_gem_free_object(struct drm_gem_object *obj)
{
struct phytium_gem_object *phytium_gem_obj = to_phytium_gem_obj(obj);
struct drm_device *dev = obj->dev;
struct phytium_display_private *priv = dev->dev_private;
uint64_t size = phytium_gem_obj->size;
DRM_DEBUG_KMS("free phytium_gem_obj iova:0x%pa size:0x%lx\n",
&phytium_gem_obj->iova, phytium_gem_obj->size);
if (phytium_gem_obj->vaddr) {
if (phytium_gem_obj->memory_type == MEMORY_TYPE_VRAM) {
phytium_memory_pool_free(priv, phytium_gem_obj->vaddr, size);
priv->mem_state[PHYTIUM_MEM_VRAM_ALLOC] -= size;
} else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_CARVEOUT) {
dma_unmap_page(dev->dev, phytium_gem_obj->iova, size, DMA_TO_DEVICE);
phytium_memory_pool_free(priv, phytium_gem_obj->vaddr, size);
priv->mem_state[PHYTIUM_MEM_SYSTEM_CARVEOUT_ALLOC] -= size;
} else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_UNIFIED) {
dma_free_attrs(dev->dev, size, phytium_gem_obj->vaddr,
phytium_gem_obj->iova, 0);
priv->mem_state[PHYTIUM_MEM_SYSTEM_UNIFIED_ALLOC] -= size;
}
list_del(&phytium_gem_obj->list);
} else if (obj->import_attach)
drm_prime_gem_destroy(obj, phytium_gem_obj->sgt);
drm_gem_object_release(obj);
kfree(phytium_gem_obj);
}
int phytium_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma)
{
int ret = 0;
struct phytium_gem_object *phytium_gem_obj = to_phytium_gem_obj(obj);
unsigned long pfn = PHYS_PFN(phytium_gem_obj->phys_addr);
/*
* Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the
* vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map
* the whole buffer.
*/
vma->vm_flags &= ~VM_PFNMAP;
vma->vm_pgoff = 0;
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
if (phytium_gem_obj->memory_type == MEMORY_TYPE_VRAM) {
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
ret = remap_pfn_range(vma, vma->vm_start, pfn,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
} else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_CARVEOUT) {
ret = remap_pfn_range(vma, vma->vm_start, pfn,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
} else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_UNIFIED) {
ret = dma_mmap_attrs(obj->dev->dev, vma, phytium_gem_obj->vaddr,
phytium_gem_obj->iova, vma->vm_end - vma->vm_start, 0);
}
if (ret)
drm_gem_vm_close(vma);
return ret;
}
int phytium_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret = 0;
ret = drm_gem_mmap(filp, vma);
if (ret < 0)
return ret;
return phytium_gem_mmap_obj(vma->vm_private_data, vma);
}
int phytium_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, uint32_t handle)
{
return drm_gem_dumb_destroy(file, dev, handle);
}
static const struct vm_operations_struct phytium_vm_ops = {
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};
static const struct drm_gem_object_funcs phytium_drm_gem_object_funcs = {
.free = phytium_gem_free_object,
.get_sg_table = phytium_gem_prime_get_sg_table,
.vmap = phytium_gem_prime_vmap,
.vunmap = phytium_gem_prime_vunmap,
.vm_ops = &phytium_vm_ops,
};
struct phytium_gem_object *phytium_gem_create_object(struct drm_device *dev, unsigned long size)
{
struct phytium_gem_object *phytium_gem_obj = NULL;
struct phytium_display_private *priv = dev->dev_private;
struct page *page = NULL;
int ret = 0;
phytium_gem_obj = kzalloc(sizeof(*phytium_gem_obj), GFP_KERNEL);
if (!phytium_gem_obj) {
DRM_ERROR("failed to allocate phytium_gem_obj\n");
ret = -ENOMEM;
goto error;
}
ret = drm_gem_object_init(dev, &phytium_gem_obj->base, size);
if (ret) {
DRM_ERROR("failed to initialize drm gem object: %d\n", ret);
goto failed_object_init;
}
if (priv->support_memory_type & MEMORY_TYPE_VRAM) {
ret = phytium_memory_pool_alloc(priv, &phytium_gem_obj->vaddr,
&phytium_gem_obj->phys_addr, size);
if (ret) {
DRM_ERROR("fail to allocate vram buffer with size %lx\n", size);
goto failed_dma_alloc;
}
phytium_gem_obj->iova = phytium_gem_obj->phys_addr;
phytium_gem_obj->memory_type = MEMORY_TYPE_VRAM;
priv->mem_state[PHYTIUM_MEM_VRAM_ALLOC] += size;
} else if (priv->support_memory_type & MEMORY_TYPE_SYSTEM_CARVEOUT) {
ret = phytium_memory_pool_alloc(priv, &phytium_gem_obj->vaddr,
&phytium_gem_obj->phys_addr, size);
if (ret) {
DRM_ERROR("fail to allocate carveout memory with size %lx\n", size);
goto failed_dma_alloc;
}
page = phys_to_page(phytium_gem_obj->phys_addr);
phytium_gem_obj->iova = dma_map_page(dev->dev, page, 0, size, DMA_TO_DEVICE);
if (dma_mapping_error(dev->dev, phytium_gem_obj->iova)) {
DRM_ERROR("fail to dma map carveout memory with size %lx\n", size);
phytium_memory_pool_free(priv, phytium_gem_obj->vaddr, size);
ret = -ENOMEM;
goto failed_dma_alloc;
}
phytium_gem_obj->memory_type = MEMORY_TYPE_SYSTEM_CARVEOUT;
priv->mem_state[PHYTIUM_MEM_SYSTEM_CARVEOUT_ALLOC] += size;
} else if (priv->support_memory_type & MEMORY_TYPE_SYSTEM_UNIFIED) {
phytium_gem_obj->vaddr = dma_alloc_attrs(dev->dev, size, &phytium_gem_obj->iova,
GFP_KERNEL, 0);
if (!phytium_gem_obj->vaddr) {
DRM_ERROR("fail to allocate unified buffer with size %lx\n", size);
ret = -ENOMEM;
goto failed_dma_alloc;
}
phytium_gem_obj->memory_type = MEMORY_TYPE_SYSTEM_UNIFIED;
priv->mem_state[PHYTIUM_MEM_SYSTEM_UNIFIED_ALLOC] += size;
} else {
DRM_ERROR("fail to allocate buffer with size %lx\n", size);
ret = -ENOMEM;
goto failed_dma_alloc;
}
phytium_gem_obj->base.funcs = &phytium_drm_gem_object_funcs;
phytium_gem_obj->size = size;
list_add_tail(&phytium_gem_obj->list, &priv->gem_list_head);
DRM_DEBUG_KMS("phytium_gem_obj iova:0x%pa size:0x%lx\n",
&phytium_gem_obj->iova, phytium_gem_obj->size);
return phytium_gem_obj;
failed_dma_alloc:
drm_gem_object_put(&phytium_gem_obj->base);
return ERR_PTR(ret);
failed_object_init:
kfree(phytium_gem_obj);
error:
return ERR_PTR(ret);
}
int phytium_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
int size = 0;
struct phytium_gem_object *phytium_gem_obj = NULL;
int ret = 0;
args->pitch = ALIGN(args->width*DIV_ROUND_UP(args->bpp, 8), 128);
args->size = args->pitch * args->height;
size = PAGE_ALIGN(args->size);
phytium_gem_obj = phytium_gem_create_object(dev, size);
if (IS_ERR(phytium_gem_obj))
return PTR_ERR(phytium_gem_obj);
ret = drm_gem_handle_create(file, &phytium_gem_obj->base, &args->handle);
if (ret) {
DRM_ERROR("failed to drm_gem_handle_create\n");
goto failed_gem_handle;
}
drm_gem_object_put(&phytium_gem_obj->base);
return 0;
failed_gem_handle:
phytium_gem_free_object(&phytium_gem_obj->base);
return ret;
}

View File

@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_GEM_H__
#define __PHYTIUM_GEM_H__
#include <drm/drm_gem.h>
struct phytium_gem_object {
struct drm_gem_object base;
phys_addr_t phys_addr;
dma_addr_t iova;
void *vaddr;
unsigned long size;
struct sg_table *sgt;
char memory_type;
char reserve[3];
struct list_head list;
void *vaddr_save;
};
#define to_phytium_gem_obj(obj) container_of(obj, struct phytium_gem_object, base)
int phytium_memory_pool_init(struct device *dev, struct phytium_display_private *priv);
void phytium_memory_pool_fini(struct device *dev, struct phytium_display_private *priv);
int phytium_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma);
int phytium_gem_mmap(struct file *filp, struct vm_area_struct *vma);
void phytium_gem_free_object(struct drm_gem_object *obj);
struct sg_table *phytium_gem_prime_get_sg_table(struct drm_gem_object *obj);
struct drm_gem_object *phytium_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach, struct sg_table *sgt);
void phytium_gem_free_object(struct drm_gem_object *obj);
int phytium_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, unsigned int handle);
struct phytium_gem_object *phytium_gem_create_object(struct drm_device *dev, unsigned long size);
int phytium_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args);
void *phytium_gem_prime_vmap(struct drm_gem_object *obj);
void phytium_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
int phytium_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
int phytium_gem_suspend(struct drm_device *drm_dev);
void phytium_gem_resume(struct drm_device *drm_dev);
#endif /* __PHYTIUM_GEM_H__ */

View File

@ -0,0 +1,420 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_modes.h>
#include "phytium_display_drv.h"
#include "phytium_dp.h"
#include "phytium_panel.h"
static int
phytium_dp_aux_set_backlight(struct phytium_panel *panel, unsigned int level)
{
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
unsigned char vals[2] = { 0x0 };
vals[0] = level;
if (phytium_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) {
vals[0] = (level & 0xFF00) >> 8;
vals[1] = (level & 0xFF);
}
if (drm_dp_dpcd_write(&phytium_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
vals, sizeof(vals)) < 0) {
DRM_DEBUG_KMS("Failed to write aux backlight level\n");
return -EIO;
}
return 0;
}
static unsigned int phytium_dp_aux_get_backlight(struct phytium_panel *panel)
{
unsigned char read_val[2] = { 0x0 };
unsigned char level = 0;
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
if (drm_dp_dpcd_read(&phytium_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
&read_val, sizeof(read_val)) < 0) {
DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
DP_EDP_BACKLIGHT_BRIGHTNESS_MSB);
return 0;
}
level = read_val[0];
if (phytium_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
level = (read_val[0] << 8 | read_val[1]);
return level;
}
static void set_aux_backlight_enable(struct phytium_panel *panel, bool enable)
{
u8 reg_val = 0;
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
if (!(phytium_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP))
return;
if (drm_dp_dpcd_readb(&phytium_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
&reg_val) < 0) {
DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
DP_EDP_DISPLAY_CONTROL_REGISTER);
return;
}
if (enable)
reg_val |= DP_EDP_BACKLIGHT_ENABLE;
else
reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE);
if (drm_dp_dpcd_writeb(&phytium_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
reg_val) != 1) {
DRM_DEBUG_KMS("Failed to %s aux backlight\n",
enable ? "enable" : "disable");
}
}
static void phytium_dp_aux_enable_backlight(struct phytium_panel *panel)
{
unsigned char dpcd_buf, new_dpcd_buf, edp_backlight_mode;
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
if (drm_dp_dpcd_readb(&phytium_dp->aux,
DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf) != 1) {
DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
return;
}
new_dpcd_buf = dpcd_buf;
edp_backlight_mode = dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
switch (edp_backlight_mode) {
case DP_EDP_BACKLIGHT_CONTROL_MODE_PWM:
case DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET:
case DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT:
new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
break;
/* Do nothing when it is already DPCD mode */
case DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD:
default:
break;
}
if (new_dpcd_buf != dpcd_buf) {
if (drm_dp_dpcd_writeb(&phytium_dp->aux,
DP_EDP_BACKLIGHT_MODE_SET_REGISTER, new_dpcd_buf) < 0) {
DRM_DEBUG_KMS("Failed to write aux backlight mode\n");
}
}
set_aux_backlight_enable(panel, true);
phytium_dp_aux_set_backlight(panel, panel->level);
}
static void phytium_dp_aux_disable_backlight(struct phytium_panel *panel)
{
set_aux_backlight_enable(panel, false);
}
static void phytium_dp_aux_setup_backlight(struct phytium_panel *panel)
{
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
if (phytium_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
phytium_dp->panel.max = 0xFFFF;
else
phytium_dp->panel.max = 0xFF;
phytium_dp->panel.min = 0;
phytium_dp->panel.level = phytium_dp_aux_get_backlight(panel);
phytium_dp->panel.backlight_enabled = (phytium_dp->panel.level != 0);
}
static void phytium_dp_hw_poweron_panel(struct phytium_panel *panel)
{
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
phytium_dp->funcs->dp_hw_poweron_panel(phytium_dp);
}
static void phytium_dp_hw_poweroff_panel(struct phytium_panel *panel)
{
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
phytium_dp->funcs->dp_hw_poweroff_panel(phytium_dp);
}
static int
phytium_dp_hw_set_backlight(struct phytium_panel *panel, uint32_t level)
{
int ret;
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
ret = phytium_dp->funcs->dp_hw_set_backlight(phytium_dp, level);
return ret;
}
static uint32_t phytium_dp_hw_get_backlight(struct phytium_panel *panel)
{
uint32_t ret;
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
ret = phytium_dp->funcs->dp_hw_get_backlight(phytium_dp);
return ret;
}
static void phytium_dp_hw_enable_backlight(struct phytium_panel *panel)
{
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
phytium_dp->funcs->dp_hw_set_backlight(phytium_dp, phytium_dp->panel.level);
phytium_dp->funcs->dp_hw_enable_backlight(phytium_dp);
}
static void phytium_dp_hw_disable_backlight(struct phytium_panel *panel)
{
struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel);
phytium_dp->funcs->dp_hw_disable_backlight(phytium_dp);
}
static void phytium_dp_hw_setup_backlight(struct phytium_panel *panel)
{
struct drm_device *dev = panel->dev;
struct phytium_display_private *priv = dev->dev_private;
panel->max = priv->info.backlight_max;
panel->min = 0;
panel->level = phytium_dp_hw_get_backlight(panel);
}
void phytium_dp_panel_init_backlight_funcs(struct phytium_dp_device *phytium_dp)
{
if (phytium_dp->edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP &&
(phytium_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP) &&
!(phytium_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP)) {
DRM_DEBUG_KMS("AUX Backlight Control Supported!\n");
phytium_dp->panel.setup_backlight = phytium_dp_aux_setup_backlight;
phytium_dp->panel.enable_backlight = phytium_dp_aux_enable_backlight;
phytium_dp->panel.disable_backlight = phytium_dp_aux_disable_backlight;
phytium_dp->panel.set_backlight = phytium_dp_aux_set_backlight;
phytium_dp->panel.get_backlight = phytium_dp_aux_get_backlight;
} else {
DRM_DEBUG_KMS("SE Backlight Control Supported!\n");
phytium_dp->panel.setup_backlight = phytium_dp_hw_setup_backlight;
phytium_dp->panel.enable_backlight = phytium_dp_hw_enable_backlight;
phytium_dp->panel.disable_backlight = phytium_dp_hw_disable_backlight;
phytium_dp->panel.set_backlight = phytium_dp_hw_set_backlight;
phytium_dp->panel.get_backlight = phytium_dp_hw_get_backlight;
}
phytium_dp->panel.poweron = phytium_dp_hw_poweron_panel;
phytium_dp->panel.poweroff = phytium_dp_hw_poweroff_panel;
mutex_init(&phytium_dp->panel.panel_lock);
phytium_dp->panel.dev = phytium_dp->dev;
/* Upper limits from eDP 1.3 spec */
phytium_dp->panel.panel_power_up_delay = 210; /* t1_t3 */
phytium_dp->panel.backlight_on_delay = 50; /* t7 */
phytium_dp->panel.backlight_off_delay = 50;
phytium_dp->panel.panel_power_down_delay = 0; /* t10 */
phytium_dp->panel.panel_power_cycle_delay = 510; /* t11 + t12 */
}
void phytium_dp_panel_release_backlight_funcs(struct phytium_dp_device *phytium_dp)
{
phytium_dp->panel.setup_backlight = NULL;
phytium_dp->panel.enable_backlight = NULL;
phytium_dp->panel.disable_backlight = NULL;
phytium_dp->panel.set_backlight = NULL;
phytium_dp->panel.get_backlight = NULL;
phytium_dp->panel.poweron = NULL;
phytium_dp->panel.poweroff = NULL;
}
void phytium_panel_enable_backlight(struct phytium_panel *panel)
{
if (panel->enable_backlight) {
mutex_lock(&panel->panel_lock);
msleep(panel->backlight_on_delay);
panel->enable_backlight(panel);
panel->backlight_enabled = true;
mutex_unlock(&panel->panel_lock);
}
}
void phytium_panel_disable_backlight(struct phytium_panel *panel)
{
if (panel->disable_backlight) {
mutex_lock(&panel->panel_lock);
panel->disable_backlight(panel);
panel->backlight_enabled = false;
msleep(panel->backlight_off_delay);
mutex_unlock(&panel->panel_lock);
}
}
void phytium_panel_poweron(struct phytium_panel *panel)
{
if (panel->poweron) {
mutex_lock(&panel->panel_lock);
panel->poweron(panel);
panel->power_enabled = true;
msleep(panel->panel_power_up_delay);
mutex_unlock(&panel->panel_lock);
}
}
void phytium_panel_poweroff(struct phytium_panel *panel)
{
if (panel->poweroff) {
mutex_lock(&panel->panel_lock);
msleep(panel->panel_power_down_delay);
panel->poweroff(panel);
panel->power_enabled = false;
mutex_unlock(&panel->panel_lock);
}
}
static uint32_t phytium_scale(uint32_t source_val,
uint32_t source_min, uint32_t source_max,
uint32_t target_min, uint32_t target_max)
{
uint64_t target_val;
WARN_ON(source_min > source_max);
WARN_ON(target_min > target_max);
/* defensive */
source_val = clamp(source_val, source_min, source_max);
/* avoid overflows */
target_val = mul_u32_u32(source_val - source_min, target_max - target_min);
target_val = DIV_ROUND_CLOSEST_ULL(target_val, source_max - source_min);
target_val += target_min;
return target_val;
}
static inline uint32_t
phytium_scale_hw_to_user(struct phytium_panel *panel, uint32_t hw_level, uint32_t user_max)
{
return phytium_scale(hw_level, panel->min, panel->max,
0, user_max);
}
static inline uint32_t
phytium_scale_user_to_hw(struct phytium_panel *panel, u32 user_level, u32 user_max)
{
return phytium_scale(user_level, 0, user_max,
panel->min, panel->max);
}
static int phytium_backlight_device_update_status(struct backlight_device *bd)
{
struct phytium_panel *panel = bl_get_data(bd);
struct drm_device *dev = panel->dev;
uint32_t hw_level = 0;
int ret = 0;
DRM_DEBUG_KMS("updating phytium_backlight, brightness=%d/%d\n",
bd->props.brightness, bd->props.max_brightness);
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
hw_level = phytium_scale_user_to_hw(panel, bd->props.brightness, bd->props.max_brightness);
if ((panel->set_backlight) && (panel->backlight_enabled)) {
mutex_lock(&panel->panel_lock);
ret = panel->set_backlight(panel, hw_level);
panel->level = hw_level;
mutex_unlock(&panel->panel_lock);
}
drm_modeset_unlock(&dev->mode_config.connection_mutex);
return ret;
}
static int phytium_backlight_device_get_brightness(struct backlight_device *bd)
{
struct phytium_panel *panel = bl_get_data(bd);
struct drm_device *dev = panel->dev;
uint32_t hw_level = 0;
int ret;
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
if (panel->get_backlight && panel->backlight_enabled) {
mutex_lock(&panel->panel_lock);
hw_level = panel->get_backlight(panel);
panel->level = hw_level;
mutex_unlock(&panel->panel_lock);
}
drm_modeset_unlock(&dev->mode_config.connection_mutex);
ret = phytium_scale_hw_to_user(panel, hw_level, bd->props.max_brightness);
DRM_DEBUG_KMS("get phytium_backlight, brightness=%d/%d\n",
ret, bd->props.max_brightness);
return ret;
}
static const struct backlight_ops phytium_backlight_device_ops = {
.update_status = phytium_backlight_device_update_status,
.get_brightness = phytium_backlight_device_get_brightness,
};
int phytium_edp_backlight_device_register(struct phytium_dp_device *phytium_dp)
{
struct backlight_properties props;
char bl_name[16];
if (phytium_dp->panel.setup_backlight) {
mutex_lock(&phytium_dp->panel.panel_lock);
phytium_dp->panel.setup_backlight(&phytium_dp->panel);
mutex_unlock(&phytium_dp->panel.panel_lock);
} else {
return -EINVAL;
}
memset(&props, 0, sizeof(props));
props.max_brightness = PHYTIUM_MAX_BL_LEVEL;
props.type = BACKLIGHT_RAW;
props.brightness = phytium_scale_hw_to_user(&phytium_dp->panel, phytium_dp->panel.level,
props.max_brightness);
snprintf(bl_name, sizeof(bl_name), "phytium_bl%d", phytium_dp->port);
phytium_dp->panel.bl_device =
backlight_device_register(bl_name,
phytium_dp->connector.kdev,
&phytium_dp->panel,
&phytium_backlight_device_ops,
&props);
if (IS_ERR(phytium_dp->panel.bl_device)) {
DRM_ERROR("Failed to register backlight: %ld\n",
PTR_ERR(phytium_dp->panel.bl_device));
phytium_dp->panel.bl_device = NULL;
return -ENODEV;
}
DRM_DEBUG_KMS("Connector %s backlight sysfs interface registered\n",
phytium_dp->connector.name);
return 0;
}
void phytium_edp_backlight_device_unregister(struct phytium_dp_device *phytium_dp)
{
if (phytium_dp->panel.bl_device) {
backlight_device_unregister(phytium_dp->panel.bl_device);
phytium_dp->panel.bl_device = NULL;
}
}

View File

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_PANEL_H__
#define __PHYTIUM_PANEL_H__
#include "phytium_dp.h"
#define PHYTIUM_MAX_BL_LEVEL 0xFF
struct phytium_panel {
struct drm_device *dev;
bool backlight_enabled;
bool power_enabled;
bool reserve1[2];
unsigned int min;
unsigned int level;
unsigned int max;
struct backlight_device *bl_device;
void (*setup_backlight)(struct phytium_panel *panel);
uint32_t (*get_backlight)(struct phytium_panel *panel);
int (*set_backlight)(struct phytium_panel *panel, uint32_t level);
void (*disable_backlight)(struct phytium_panel *panel);
void (*enable_backlight)(struct phytium_panel *panel);
void (*poweron)(struct phytium_panel *panel);
void (*poweroff)(struct phytium_panel *panel);
struct mutex panel_lock;
uint32_t panel_power_up_delay;
uint32_t backlight_on_delay;
uint32_t backlight_off_delay;
uint32_t panel_power_down_delay;
uint32_t panel_power_cycle_delay;
};
void phytium_dp_panel_init_backlight_funcs(struct phytium_dp_device *phytium_dp);
void phytium_panel_release_backlight_funcs(struct phytium_dp_device *phytium_dp);
int phytium_edp_backlight_device_register(struct phytium_dp_device *phytium_dp);
void phytium_edp_backlight_device_unregister(struct phytium_dp_device *phytium_dp);
void phytium_panel_enable_backlight(struct phytium_panel *panel);
void phytium_panel_disable_backlight(struct phytium_panel *panel);
void phytium_panel_poweron(struct phytium_panel *panel);
void phytium_panel_poweroff(struct phytium_panel *panel);
#endif /* __PHYTIUM_PANEL_H__ */

View File

@ -0,0 +1,388 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <linux/pci.h>
#include <drm/drm_drv.h>
#include <linux/dmaengine.h>
#include "phytium_display_drv.h"
#include "phytium_pci.h"
#include "phytium_dp.h"
#include "phytium_gem.h"
#include "px210_dc.h"
#include "px210_dp.h"
#include "pe220x_dc.h"
#include "pe220x_dp.h"
int dc_msi_enable;
module_param(dc_msi_enable, int, 0644);
MODULE_PARM_DESC(dc_msi_enable, "Enable DC msi interrupt (0-disabled; 1-enabled; default-0)");
void phytium_pci_vram_hw_init(struct phytium_display_private *priv)
{
struct phytium_pci_private *pci_priv = to_pci_priv(priv);
pci_priv->dc_hw_vram_init(priv, priv->pool_phys_addr, priv->pool_size);
}
int phytium_pci_vram_init(struct pci_dev *pdev, struct phytium_display_private *priv)
{
int ret = 0;
priv->pool_phys_addr = pci_resource_start(pdev, 2);
priv->pool_size = pci_resource_len(pdev, 2);
if ((priv->pool_phys_addr != 0) && (priv->pool_size != 0)) {
priv->pool_virt_addr = devm_ioremap_wc(&pdev->dev, priv->pool_phys_addr,
priv->pool_size);
if (priv->pool_virt_addr == NULL) {
DRM_ERROR("pci vram ioremap fail, addr:0x%llx, size:0x%llx\n",
priv->pool_phys_addr, priv->pool_size);
ret = -EINVAL;
goto failed_ioremap;
}
ret = phytium_memory_pool_init(&pdev->dev, priv);
if (ret)
goto failed_init_memory_pool;
priv->mem_state[PHYTIUM_MEM_VRAM_TOTAL] = priv->pool_size;
priv->support_memory_type = MEMORY_TYPE_VRAM;
priv->vram_hw_init = phytium_pci_vram_hw_init;
} else {
DRM_DEBUG_KMS("not support vram\n");
priv->pool_virt_addr = NULL;
priv->mem_state[PHYTIUM_MEM_VRAM_TOTAL] = 0;
priv->support_memory_type = MEMORY_TYPE_SYSTEM_UNIFIED;
priv->vram_hw_init = NULL;
}
return 0;
failed_init_memory_pool:
devm_iounmap(&pdev->dev, priv->pool_virt_addr);
failed_ioremap:
return ret;
}
void phytium_pci_vram_fini(struct pci_dev *pdev, struct phytium_display_private *priv)
{
if (priv->support_memory_type == MEMORY_TYPE_VRAM) {
phytium_memory_pool_fini(&pdev->dev, priv);
devm_iounmap(&pdev->dev, priv->pool_virt_addr);
}
}
static bool phytium_pci_dma_chan_filter(struct dma_chan *chan, void *param)
{
struct phytium_dma_slave *s = param;
if (s->dma_dev != chan->device->dev)
return false;
if (s->chan_id == chan->chan_id)
return true;
else
return false;
}
int phytium_pci_dma_init(struct phytium_display_private *priv)
{
struct pci_dev *dma_dev, *gpu_dev;
struct drm_device *drm_dev = priv->dev;
dma_cap_mask_t mask;
struct phytium_dma_slave s;
int ret = 0;
u16 cmd;
/* check px210 gpu enable */
gpu_dev = pci_get_device(PCI_VENDOR_ID_PHYTIUM, 0xdc20, NULL);
if (!gpu_dev) {
DRM_INFO("failed to get gpu_dev\n");
ret = -ENODEV;
goto failed;
}
pci_read_config_word(gpu_dev, PCI_COMMAND, &cmd);
if (!(cmd & PCI_COMMAND_MASTER)) {
DRM_INFO("gpu_dev master is disabled\n");
ret = -ENODEV;
goto failed;
}
dma_dev = pci_get_device(PCI_VENDOR_ID_PHYTIUM, 0xdc3c, NULL);
if (!dma_dev) {
DRM_INFO("failed to get dma_dev\n");
ret = -ENODEV;
goto failed;
}
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
s.dma_dev = &dma_dev->dev;
s.chan_id = 2;
priv->dma_chan = dma_request_channel(mask, phytium_pci_dma_chan_filter, &s);
if (!priv->dma_chan) {
DRM_DEV_ERROR(drm_dev->dev, "failed to request dma chan\n");
ret = -EBUSY;
goto failed;
}
priv->dma_inited = 1;
failed:
return ret;
}
void phytium_pci_dma_fini(struct phytium_display_private *priv)
{
if (priv->dma_inited)
dma_release_channel(priv->dma_chan);
priv->dma_inited = 0;
priv->dma_chan = NULL;
}
static struct phytium_display_private*
phytium_pci_private_init(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct phytium_display_private *priv = NULL;
struct phytium_pci_private *pci_priv = NULL;
struct phytium_device_info *phytium_info = (struct phytium_device_info *)ent->driver_data;
int i = 0;
resource_size_t io_addr, io_size;
pci_priv = devm_kzalloc(&pdev->dev, sizeof(*pci_priv), GFP_KERNEL);
if (!pci_priv) {
DRM_ERROR("no memory to allocate for drm_display_private\n");
goto failed_malloc_priv;
}
memset(pci_priv, 0, sizeof(*pci_priv));
priv = &pci_priv->base;
phytium_display_private_init(priv, dev);
memcpy(&(priv->info), phytium_info, sizeof(struct phytium_device_info));
DRM_DEBUG_KMS("priv->info.num_pipes :%d\n", priv->info.num_pipes);
priv->info.pipe_mask = ((pdev->subsystem_device >> PIPE_MASK_SHIFT) & PIPE_MASK_MASK);
priv->info.edp_mask = ((pdev->subsystem_device >> EDP_MASK_SHIFT) & EDP_MASK_MASK);
priv->info.num_pipes = 0;
for_each_pipe_masked(priv, i)
priv->info.num_pipes++;
if (priv->info.num_pipes == 0) {
DRM_ERROR("num_pipes is zero, so exit init\n");
goto failed_init_numpipe;
}
io_addr = pci_resource_start(pdev, 0);
io_size = pci_resource_len(pdev, 0);
priv->regs = ioremap(io_addr, io_size);
if (priv->regs == NULL) {
DRM_ERROR("pci bar0 ioremap fail, addr:0x%llx, size:0x%llx\n", io_addr, io_size);
goto failed_ioremap;
}
priv->irq = pdev->irq;
if (IS_PX210(priv)) {
pci_priv->dc_hw_vram_init = px210_dc_hw_vram_init;
priv->dc_hw_clear_msi_irq = px210_dc_hw_clear_msi_irq;
priv->dc_hw_fb_format_check = px210_dc_hw_fb_format_check;
} else if (IS_PE220X(priv)) {
pci_priv->dc_hw_vram_init = pe220x_dc_hw_vram_init;
priv->dc_hw_clear_msi_irq = NULL;
priv->dc_hw_fb_format_check = pe220x_dc_hw_fb_format_check;
}
return priv;
failed_ioremap:
failed_init_numpipe:
devm_kfree(&pdev->dev, pci_priv);
failed_malloc_priv:
return NULL;
}
static void
phytium_pci_private_fini(struct pci_dev *pdev, struct phytium_display_private *priv)
{
struct phytium_pci_private *pci_priv = to_pci_priv(priv);
if (priv->regs)
iounmap(priv->regs);
devm_kfree(&pdev->dev, pci_priv);
}
static int phytium_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct phytium_display_private *priv = NULL;
struct drm_device *dev = NULL;
int ret = 0;
dev = drm_dev_alloc(&phytium_display_drm_driver, &pdev->dev);
if (IS_ERR(dev)) {
DRM_ERROR("failed to allocate drm_device\n");
return PTR_ERR(dev);
}
dev->pdev = pdev;
pci_set_drvdata(pdev, dev);
pci_set_master(pdev);
ret = pci_enable_device(pdev);
if (ret) {
DRM_ERROR("pci enbale device fail\n");
goto failed_enable_device;
}
if (dc_msi_enable) {
ret = pci_enable_msi(pdev);
if (ret)
DRM_ERROR("pci enbale msi fail\n");
}
dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
priv = phytium_pci_private_init(pdev, ent);
if (priv)
dev->dev_private = priv;
else
goto failed_pci_private_init;
ret = phytium_pci_vram_init(pdev, priv);
if (ret) {
DRM_ERROR("failed to init pci vram\n");
goto failed_pci_vram_init;
}
ret = drm_dev_register(dev, 0);
if (ret) {
DRM_ERROR("failed to register drm dev\n");
goto failed_register_drm;
}
phytium_dp_hpd_irq_setup(dev, true);
return 0;
failed_register_drm:
phytium_pci_vram_fini(pdev, priv);
failed_pci_vram_init:
phytium_pci_private_fini(pdev, priv);
failed_pci_private_init:
if (pdev->msi_enabled)
pci_disable_msi(pdev);
pci_disable_device(pdev);
failed_enable_device:
pci_set_drvdata(pdev, NULL);
drm_dev_put(dev);
return -1;
}
static void phytium_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct phytium_display_private *priv = dev->dev_private;
phytium_dp_hpd_irq_setup(dev, false);
cancel_work_sync(&priv->hotplug_work);
drm_dev_unregister(dev);
phytium_pci_vram_fini(pdev, priv);
phytium_pci_private_fini(pdev, priv);
if (pdev->msi_enabled)
pci_disable_msi(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
drm_dev_put(dev);
}
static void phytium_pci_shutdown(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct phytium_display_private *priv = dev->dev_private;
priv->display_shutdown(dev);
}
static int phytium_pci_pm_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
struct phytium_display_private *priv = drm_dev->dev_private;
int ret = 0;
if (IS_PX210(priv))
phytium_pci_dma_init(priv);
ret = priv->display_pm_suspend(drm_dev);
if (ret < 0)
goto out;
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
udelay(200);
out:
return ret;
}
static int phytium_pci_pm_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
struct phytium_display_private *priv = drm_dev->dev_private;
int ret = 0;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
ret = pci_enable_device(pdev);
if (ret)
return ret;
pci_set_master(pdev);
ret = priv->display_pm_resume(drm_dev);
if (IS_PX210(priv))
phytium_pci_dma_fini(priv);
return ret;
}
static const struct dev_pm_ops phytium_pci_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(phytium_pci_pm_suspend, phytium_pci_pm_resume)
};
static const struct phytium_device_info px210_info = {
.platform_mask = BIT(PHYTIUM_PLATFORM_PX210),
.total_pipes = 3,
.crtc_clock_max = PX210_DC_PIX_CLOCK_MAX,
.hdisplay_max = PX210_DC_HDISPLAY_MAX,
.vdisplay_max = PX210_DC_VDISPLAY_MAX,
.address_mask = PX210_DC_ADDRESS_MASK,
.backlight_max = PX210_DP_BACKLIGHT_MAX,
};
static const struct phytium_device_info pe220x_info = {
.platform_mask = BIT(PHYTIUM_PLATFORM_PE220X),
.total_pipes = 2,
.crtc_clock_max = PE220X_DC_PIX_CLOCK_MAX,
.hdisplay_max = PE220X_DC_HDISPLAY_MAX,
.vdisplay_max = PE220X_DC_VDISPLAY_MAX,
.address_mask = PE220X_DC_ADDRESS_MASK,
.backlight_max = PE220X_DP_BACKLIGHT_MAX,
};
static const struct pci_device_id phytium_display_pci_ids[] = {
{ PCI_VDEVICE(PHYTIUM, 0xdc22), (kernel_ulong_t)&px210_info },
{ PCI_VDEVICE(PHYTIUM, 0xdc3e), (kernel_ulong_t)&pe220x_info },
{ /* End: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, phytium_display_pci_ids);
struct pci_driver phytium_pci_driver = {
.name = "phytium_display_pci",
.id_table = phytium_display_pci_ids,
.probe = phytium_pci_probe,
.remove = phytium_pci_remove,
.shutdown = phytium_pci_shutdown,
.driver.pm = &phytium_pci_pm_ops,
};

View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_PCI_H__
#define __PHYTIUM_PCI_H__
#include "phytium_display_drv.h"
struct phytium_pci_private {
struct phytium_display_private base;
void (*dc_hw_vram_init)(struct phytium_display_private *priv, resource_size_t vram_addr,
resource_size_t vram_size);
};
struct phytium_dma_slave {
struct device *dma_dev;
u32 chan_id;
};
#define to_pci_priv(priv) container_of(priv, struct phytium_pci_private, base)
extern struct pci_driver phytium_pci_driver;
#endif /* __PHYTIUM_PCI_H__ */

View File

@ -0,0 +1,643 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_fourcc.h>
#include <linux/dma-buf.h>
#include "phytium_display_drv.h"
#include "phytium_plane.h"
#include "phytium_fb.h"
#include "phytium_gem.h"
#include "phytium_crtc.h"
#include "px210_dc.h"
#include "pe220x_dc.h"
#include "phytium_reg.h"
#define PHYTIUM_CURS_W_SIZE 32
#define PHYTIUM_CURS_H_SIZE 32
void phytium_plane_destroy(struct drm_plane *plane)
{
struct phytium_plane *phytium_plane = to_phytium_plane(plane);
drm_plane_cleanup(plane);
kfree(phytium_plane);
}
/**
* phytium_plane_atomic_get_property - fetch plane property value
* @plane: plane to fetch property for
* @state: state containing the property value
* @property: property to look up
* @val: pointer to write property value into
*
* The DRM core does not store shadow copies of properties for
* atomic-capable drivers. This entrypoint is used to fetch
* the current value of a driver-specific plane property.
*/
static int
phytium_plane_atomic_get_property(struct drm_plane *plane,
const struct drm_plane_state *state,
struct drm_property *property,
uint64_t *val)
{
DRM_DEBUG_KMS("Unknown plane property [PROP:%d:%s]\n", property->base.id, property->name);
return -EINVAL;
}
/**
* phytium_plane_atomic_set_property - set plane property value
* @plane: plane to set property for
* @state: state to update property value in
* @property: property to set
* @val: value to set property to
*
* Writes the specified property value for a plane into the provided atomic
* state object.
*
* Returns 0 on success, -EINVAL on unrecognized properties
*/
int
phytium_plane_atomic_set_property(struct drm_plane *plane,
struct drm_plane_state *state,
struct drm_property *property,
uint64_t val)
{
DRM_DEBUG_KMS("Unknown plane property [PROP:%d:%s]\n", property->base.id, property->name);
return -EINVAL;
}
struct drm_plane_state *
phytium_plane_atomic_duplicate_state(struct drm_plane *plane)
{
struct drm_plane_state *state = NULL;
struct phytium_plane_state *phytium_state = NULL;
phytium_state = kmemdup(plane->state, sizeof(*phytium_state), GFP_KERNEL);
if (!phytium_state)
return NULL;
state = &phytium_state->base;
if (state->fb)
drm_framebuffer_get(state->fb);
state->fence = NULL;
state->commit = NULL;
return state;
}
void
phytium_plane_atomic_destroy_state(struct drm_plane *plane, struct drm_plane_state *state)
{
struct phytium_plane_state *phytium_state = to_phytium_plane_state(state);
__drm_atomic_helper_plane_destroy_state(state);
kfree(phytium_state);
}
const struct drm_plane_funcs phytium_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = phytium_plane_destroy,
.reset = drm_atomic_helper_plane_reset,
.atomic_get_property = phytium_plane_atomic_get_property,
.atomic_set_property = phytium_plane_atomic_set_property,
.atomic_duplicate_state = phytium_plane_atomic_duplicate_state,
.atomic_destroy_state = phytium_plane_atomic_destroy_state,
};
static int phytium_plane_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct dma_buf *dma_buf;
struct dma_fence *fence;
if (!state->fb)
return 0;
dma_buf = to_phytium_framebuffer(state->fb)->phytium_gem_obj[0]->base.dma_buf;
if (dma_buf) {
fence = dma_resv_get_excl_rcu(dma_buf->resv);
drm_atomic_set_fence_for_plane(state, fence);
}
return 0;
}
static int
phytium_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state)
{
struct drm_device *dev = plane->dev;
struct phytium_display_private *priv = dev->dev_private;
struct drm_framebuffer *fb = state->fb;
struct drm_crtc *crtc = state->crtc;
struct drm_crtc_state *crtc_state;
int src_x, src_y, src_w, src_h;
unsigned long base_offset;
struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc);
if ((!fb) || (!crtc))
return 0;
crtc_state = drm_atomic_get_crtc_state(state->state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
if (plane->type == DRM_PLANE_TYPE_CURSOR) {
src_w = state->src_w >> 16;
src_h = state->src_h >> 16;
if (phytium_crtc->scale_enable)
return -EINVAL;
if ((src_w != PHYTIUM_CURS_W_SIZE) || (src_h != PHYTIUM_CURS_W_SIZE)) {
DRM_INFO("Invalid cursor size(%d, %d)\n", src_w, src_h);
return -EINVAL;
}
} else if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
src_x = state->src_x >> 16;
src_y = state->src_y >> 16;
src_w = state->src_w >> 16;
src_h = state->src_h >> 16;
base_offset = src_x * fb->format->cpp[0] + src_y*fb->pitches[0];
if (base_offset & (priv->info.address_mask)) {
DRM_ERROR("fb base address is not aligned by 0x%lx byte\n",
priv->info.address_mask);
return -EINVAL;
}
if (src_w != state->crtc_w || src_h != state->crtc_h) {
DRM_ERROR("scale not support: crtc_w(0x%x)/h(0x%x) src_w(0x%x)/h(0x%x)\n",
state->crtc_w, state->crtc_h, src_w, src_h);
return -EINVAL;
}
if ((state->crtc_x < 0) || (state->crtc_y < 0)) {
DRM_ERROR("crtc_x(0x%x)/y(0x%x) of drm plane state is invalid\n",
state->crtc_x, state->crtc_y);
return -EINVAL;
}
if ((state->crtc_x + state->crtc_w > crtc_state->adjusted_mode.hdisplay)
|| (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay)) {
DRM_ERROR("plane out of crtc region\n");
return -EINVAL;
}
}
return 0;
}
static void phytium_dc_get_plane_parameter(struct drm_plane *plane)
{
struct phytium_plane *phytium_plane = to_phytium_plane(plane);
struct drm_framebuffer *fb = plane->state->fb;
struct phytium_framebuffer *phytium_fb = to_phytium_framebuffer(fb);
struct phytium_gem_object *phytium_gem_obj = NULL;
int i, num_planes = 0;
const struct drm_format_info *info;
info = drm_format_info(fb->format->format);
num_planes = info ? info->num_planes : 1;
for (i = 0; i < num_planes; i++) {
phytium_gem_obj = phytium_fb->phytium_gem_obj[i];
phytium_plane->iova[i] = phytium_gem_obj->iova + fb->offsets[i];
phytium_plane->size[i] = phytium_gem_obj->size - fb->offsets[i];
if (fb->modifier == DRM_FORMAT_MOD_PHYTIUM_TILE_MODE0_FBCDC)
phytium_plane->tiling[i] = FRAMEBUFFER_TILE_MODE0;
else if (fb->modifier == DRM_FORMAT_MOD_PHYTIUM_TILE_MODE3_FBCDC)
phytium_plane->tiling[i] = FRAMEBUFFER_TILE_MODE3;
else if (fb->modifier == DRM_FORMAT_MOD_LINEAR)
phytium_plane->tiling[i] = FRAMEBUFFER_LINEAR;
else
phytium_plane->tiling[i] = FRAMEBUFFER_LINEAR;
if (i == 0) {
switch (fb->format->format) {
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_BGRA1010102:
phytium_plane->format = FRAMEBUFFER_FORMAT_ARGB2101010;
break;
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_BGRA8888:
phytium_plane->format = FRAMEBUFFER_FORMAT_ARGB8888;
break;
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_BGRX8888:
phytium_plane->format = FRAMEBUFFER_FORMAT_XRGB8888;
break;
case DRM_FORMAT_ARGB4444:
case DRM_FORMAT_ABGR4444:
case DRM_FORMAT_RGBA4444:
case DRM_FORMAT_BGRA4444:
phytium_plane->format = FRAMEBUFFER_FORMAT_ARGB4444;
break;
case DRM_FORMAT_XRGB4444:
case DRM_FORMAT_XBGR4444:
case DRM_FORMAT_RGBX4444:
case DRM_FORMAT_BGRX4444:
phytium_plane->format = FRAMEBUFFER_FORMAT_XRGB4444;
break;
case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_ABGR1555:
case DRM_FORMAT_RGBA5551:
case DRM_FORMAT_BGRA5551:
phytium_plane->format = FRAMEBUFFER_FORMAT_ARGB1555;
break;
case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_XBGR1555:
case DRM_FORMAT_RGBX5551:
case DRM_FORMAT_BGRX5551:
phytium_plane->format = FRAMEBUFFER_FORMAT_XRGB1555;
break;
case DRM_FORMAT_RGB565:
case DRM_FORMAT_BGR565:
phytium_plane->format = FRAMEBUFFER_FORMAT_RGB565;
break;
case DRM_FORMAT_YUYV:
phytium_plane->format = FRAMEBUFFER_FORMAT_YUYV;
break;
case DRM_FORMAT_UYVY:
phytium_plane->format = FRAMEBUFFER_FORMAT_UYVY;
break;
case DRM_FORMAT_NV16:
phytium_plane->format = FRAMEBUFFER_FORMAT_NV16;
break;
case DRM_FORMAT_NV12:
phytium_plane->format = FRAMEBUFFER_FORMAT_NV12;
break;
case DRM_FORMAT_NV21:
phytium_plane->format = FRAMEBUFFER_FORMAT_NV12;
break;
default:
DRM_ERROR("unsupported pixel format (format = %d)\n",
fb->format->format);
return;
}
switch (fb->format->format) {
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_ARGB4444:
case DRM_FORMAT_XRGB4444:
case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_RGB565:
phytium_plane->swizzle = FRAMEBUFFER_SWIZZLE_ARGB;
phytium_plane->uv_swizzle = FRAMEBUFFER_UVSWIZZLE_DISABLE;
break;
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_ABGR4444:
case DRM_FORMAT_XBGR4444:
case DRM_FORMAT_ABGR1555:
case DRM_FORMAT_XBGR1555:
case DRM_FORMAT_BGR565:
phytium_plane->swizzle = FRAMEBUFFER_SWIZZLE_ABGR;
phytium_plane->uv_swizzle = FRAMEBUFFER_UVSWIZZLE_DISABLE;
break;
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_RGBA4444:
case DRM_FORMAT_RGBX4444:
case DRM_FORMAT_RGBA5551:
case DRM_FORMAT_RGBX5551:
phytium_plane->swizzle = FRAMEBUFFER_SWIZZLE_RGBA;
phytium_plane->uv_swizzle = FRAMEBUFFER_UVSWIZZLE_DISABLE;
break;
case DRM_FORMAT_BGRA1010102:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_BGRX8888:
case DRM_FORMAT_BGRA4444:
case DRM_FORMAT_BGRX4444:
case DRM_FORMAT_BGRA5551:
case DRM_FORMAT_BGRX5551:
phytium_plane->swizzle = FRAMEBUFFER_SWIZZLE_BGRA;
phytium_plane->uv_swizzle = FRAMEBUFFER_UVSWIZZLE_DISABLE;
break;
case DRM_FORMAT_YUYV:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV12:
phytium_plane->swizzle = FRAMEBUFFER_SWIZZLE_ARGB;
phytium_plane->uv_swizzle = FRAMEBUFFER_UVSWIZZLE_DISABLE;
break;
default:
DRM_ERROR("unsupported pixel format (format = %d)\n",
fb->format->format);
return;
}
}
}
}
static void phytium_dc_primary_plane_update(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);
struct drm_framebuffer *fb = plane->state->fb;
int phys_pipe = phytium_plane->phys_pipe;
int src_x, src_y, crtc_x, crtc_y, crtc_w, crtc_h;
unsigned long base_offset;
int config;
src_x = plane->state->src_x >> 16;
src_y = plane->state->src_y >> 16;
crtc_x = plane->state->crtc_x;
crtc_y = plane->state->crtc_y;
crtc_w = plane->state->crtc_w;
crtc_h = plane->state->crtc_h;
if (phytium_plane->dc_hw_update_dcreq)
phytium_plane->dc_hw_update_dcreq(plane);
phytium_plane->dc_hw_update_primary_hi_addr(plane);
/* config dc */
/* Y */
base_offset = src_x * fb->format->cpp[0] + src_y*fb->pitches[0];
phytium_writel_reg(priv, (phytium_plane->iova[0] + base_offset) & ADDRESS_MASK,
priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_Y_ADDRESS);
phytium_writel_reg(priv, ALIGN(fb->pitches[0], 128),
priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_Y_STRIDE);
/* U */
phytium_writel_reg(priv, phytium_plane->iova[1] & 0xffffffff,
priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_U_ADDRESS);
phytium_writel_reg(priv, ALIGN(fb->pitches[1], 128),
priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_U_STRIDE);
/* V */
phytium_writel_reg(priv, phytium_plane->iova[2] & 0xffffffff,
priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_V_ADDRESS);
phytium_writel_reg(priv, ALIGN(fb->pitches[2], 128),
priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_V_STRIDE);
/* size */
phytium_writel_reg(priv, (crtc_w & WIDTH_MASK) | ((crtc_h&HEIGHT_MASK) << HEIGHT_SHIFT),
priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_SIZE);
/* config */
config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe],
PHYTIUM_DC_FRAMEBUFFER_CONFIG);
config &= ~(FRAMEBUFFER_FORMAT_MASK << FRAMEBUFFER_FORMAT_SHIFT);
config |= (phytium_plane->format << FRAMEBUFFER_FORMAT_SHIFT);
config &= ~(1 << FRAMEBUFFER_UVSWIZZLE_SHIFT);
config |= (phytium_plane->uv_swizzle << FRAMEBUFFER_UVSWIZZLE_SHIFT);
config &= ~(FRAMEBUFFER_SWIZZLE_MASK << FRAMEBUFFER_SWIZZLE_SHIFT);
config |= (phytium_plane->swizzle << FRAMEBUFFER_SWIZZLE_SHIFT);
config &= ~(FRAMEBUFFER_TILE_MODE_MASK << FRAMEBUFFER_TILE_MODE_SHIFT);
config |= (phytium_plane->tiling[0] << FRAMEBUFFER_TILE_MODE_SHIFT);
config &= (~FRAMEBUFFER_CLEAR);
phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe],
PHYTIUM_DC_FRAMEBUFFER_CONFIG);
}
static void phytium_dc_cursor_plane_update(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);
struct drm_framebuffer *fb = plane->state->fb;
int phys_pipe = phytium_plane->phys_pipe;
int config;
unsigned long iova;
phytium_plane->enable = 1;
phytium_plane->cursor_hot_x = fb->hot_x;
phytium_plane->cursor_hot_y = fb->hot_y;
phytium_plane->cursor_x = plane->state->crtc_x + fb->hot_x;
phytium_plane->cursor_y = plane->state->crtc_y + fb->hot_y;
config = CURSOR_FORMAT_ARGB8888 |
((phytium_plane->cursor_hot_y & CURSOR_HOT_Y_MASK) << CURSOR_HOT_Y_SHIFT) |
((phytium_plane->cursor_hot_x & CURSOR_HOT_X_MASK) << CURSOR_HOT_X_SHIFT);
phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe], PHYTIUM_DC_CURSOR_CONFIG);
config = ((phytium_plane->cursor_x & CURSOR_X_MASK) << CURSOR_X_SHIFT) |
((phytium_plane->cursor_y & CURSOR_Y_MASK) << CURSOR_Y_SHIFT);
phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe],
PHYTIUM_DC_CURSOR_LOCATION);
iova = phytium_plane->iova[0];
phytium_writel_reg(priv, iova & 0xffffffff, priv->dc_reg_base[phys_pipe],
PHYTIUM_DC_CURSOR_ADDRESS);
if (phytium_plane->dc_hw_update_cursor_hi_addr)
phytium_plane->dc_hw_update_cursor_hi_addr(plane, iova);
}
static void phytium_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct drm_framebuffer *fb, *old_fb;
DRM_DEBUG_KMS("update plane: type=%d\n", plane->type);
if (!plane->state->crtc || !plane->state->fb)
return;
fb = plane->state->fb;
old_fb = old_state->fb;
if (fb)
drm_framebuffer_get(fb);
if (old_fb)
drm_framebuffer_put(old_fb);
phytium_dc_get_plane_parameter(plane);
if (plane->type == DRM_PLANE_TYPE_PRIMARY)
phytium_dc_primary_plane_update(plane);
else if (plane->type == DRM_PLANE_TYPE_CURSOR)
phytium_dc_cursor_plane_update(plane);
}
static void phytium_plane_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
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;
struct drm_framebuffer *old_fb;
old_fb = old_state->fb;
if (old_fb)
drm_framebuffer_put(old_fb);
if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
phytium_writel_reg(priv, CLEAR_VALUE_RED, 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);
} else if (plane->type == DRM_PLANE_TYPE_CURSOR) {
phytium_writel_reg(priv, CURSOR_FORMAT_DISABLED,
priv->dc_reg_base[phys_pipe], PHYTIUM_DC_CURSOR_CONFIG);
}
}
const struct drm_plane_helper_funcs phytium_plane_helper_funcs = {
.prepare_fb = phytium_plane_prepare_fb,
.atomic_check = phytium_plane_atomic_check,
.atomic_update = phytium_plane_atomic_update,
.atomic_disable = phytium_plane_atomic_disable,
};
struct phytium_plane *phytium_primary_plane_create(struct drm_device *dev, int phys_pipe)
{
struct phytium_display_private *priv = dev->dev_private;
struct phytium_plane *phytium_plane = NULL;
struct phytium_plane_state *phytium_plane_state = NULL;
int ret = 0;
unsigned int flags = 0;
const uint32_t *formats = NULL;
uint32_t format_count;
const uint64_t *format_modifiers;
phytium_plane = kzalloc(sizeof(*phytium_plane), GFP_KERNEL);
if (!phytium_plane) {
ret = -ENOMEM;
goto failed_malloc_plane;
}
phytium_plane_state = kzalloc(sizeof(*phytium_plane_state), GFP_KERNEL);
if (!phytium_plane_state) {
ret = -ENOMEM;
goto failed_malloc_plane_state;
}
phytium_plane_state->base.plane = &phytium_plane->base;
phytium_plane_state->base.rotation = DRM_MODE_ROTATE_0;
phytium_plane->base.state = &phytium_plane_state->base;
phytium_plane->phys_pipe = phys_pipe;
if (IS_PX210(priv)) {
phytium_plane->dc_hw_plane_get_format = px210_dc_hw_plane_get_primary_format;
phytium_plane->dc_hw_update_dcreq = px210_dc_hw_update_dcreq;
phytium_plane->dc_hw_update_primary_hi_addr = px210_dc_hw_update_primary_hi_addr;
phytium_plane->dc_hw_update_cursor_hi_addr = NULL;
} else if (IS_PE220X(priv)) {
phytium_plane->dc_hw_plane_get_format = pe220x_dc_hw_plane_get_primary_format;
phytium_plane->dc_hw_update_dcreq = NULL;
phytium_plane->dc_hw_update_primary_hi_addr = pe220x_dc_hw_update_primary_hi_addr;
phytium_plane->dc_hw_update_cursor_hi_addr = NULL;
}
phytium_plane->dc_hw_plane_get_format(&format_modifiers, &formats, &format_count);
ret = drm_universal_plane_init(dev, &phytium_plane->base, 0x0,
&phytium_plane_funcs, formats,
format_count,
format_modifiers,
DRM_PLANE_TYPE_PRIMARY, "primary %d", phys_pipe);
if (ret)
goto failed_plane_init;
flags = DRM_MODE_ROTATE_0;
drm_plane_create_rotation_property(&phytium_plane->base, DRM_MODE_ROTATE_0, flags);
drm_plane_helper_add(&phytium_plane->base, &phytium_plane_helper_funcs);
return phytium_plane;
failed_plane_init:
kfree(phytium_plane_state);
failed_malloc_plane_state:
kfree(phytium_plane);
failed_malloc_plane:
return ERR_PTR(ret);
}
struct phytium_plane *phytium_cursor_plane_create(struct drm_device *dev, int phys_pipe)
{
struct phytium_display_private *priv = dev->dev_private;
struct phytium_plane *phytium_plane = NULL;
struct phytium_plane_state *phytium_plane_state = NULL;
int ret = 0;
unsigned int flags = 0;
const uint32_t *formats = NULL;
uint32_t format_count;
const uint64_t *format_modifiers;
phytium_plane = kzalloc(sizeof(*phytium_plane), GFP_KERNEL);
if (!phytium_plane) {
ret = -ENOMEM;
goto failed_malloc_plane;
}
phytium_plane_state = kzalloc(sizeof(*phytium_plane_state), GFP_KERNEL);
if (!phytium_plane_state) {
ret = -ENOMEM;
goto failed_malloc_plane_state;
}
phytium_plane_state->base.plane = &phytium_plane->base;
phytium_plane_state->base.rotation = DRM_MODE_ROTATE_0;
phytium_plane->base.state = &phytium_plane_state->base;
phytium_plane->phys_pipe = phys_pipe;
if (IS_PX210(priv)) {
phytium_plane->dc_hw_plane_get_format = px210_dc_hw_plane_get_cursor_format;
phytium_plane->dc_hw_update_dcreq = NULL;
phytium_plane->dc_hw_update_primary_hi_addr = NULL;
phytium_plane->dc_hw_update_cursor_hi_addr = NULL;
} else if (IS_PE220X(priv)) {
phytium_plane->dc_hw_plane_get_format = pe220x_dc_hw_plane_get_cursor_format;
phytium_plane->dc_hw_update_dcreq = NULL;
phytium_plane->dc_hw_update_primary_hi_addr = NULL;
phytium_plane->dc_hw_update_cursor_hi_addr = pe220x_dc_hw_update_cursor_hi_addr;
}
phytium_plane->dc_hw_plane_get_format(&format_modifiers, &formats, &format_count);
ret = drm_universal_plane_init(dev, &phytium_plane->base, 0x0,
&phytium_plane_funcs,
formats, format_count,
format_modifiers,
DRM_PLANE_TYPE_CURSOR, "cursor %d", phys_pipe);
if (ret)
goto failed_plane_init;
flags = DRM_MODE_ROTATE_0;
drm_plane_create_rotation_property(&phytium_plane->base, DRM_MODE_ROTATE_0, flags);
drm_plane_helper_add(&phytium_plane->base, &phytium_plane_helper_funcs);
return phytium_plane;
failed_plane_init:
kfree(phytium_plane_state);
failed_malloc_plane_state:
kfree(phytium_plane);
failed_malloc_plane:
return ERR_PTR(ret);
}

View File

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_PLANE_H__
#define __PHYTIUM_PLANE_H__
struct phytium_plane {
struct drm_plane base;
int phys_pipe;
unsigned long iova[PHYTIUM_FORMAT_MAX_PLANE];
unsigned long size[PHYTIUM_FORMAT_MAX_PLANE];
unsigned int format;
unsigned int tiling[PHYTIUM_FORMAT_MAX_PLANE];
unsigned int swizzle;
unsigned int uv_swizzle;
unsigned int rot_angle;
/* only for cursor */
bool enable;
bool reserve[3];
unsigned int cursor_x;
unsigned int cursor_y;
unsigned int cursor_hot_x;
unsigned int cursor_hot_y;
void (*dc_hw_plane_get_format)(const uint64_t **format_modifiers,
const uint32_t **formats,
uint32_t *format_count);
void (*dc_hw_update_dcreq)(struct drm_plane *plane);
void (*dc_hw_update_primary_hi_addr)(struct drm_plane *plane);
void (*dc_hw_update_cursor_hi_addr)(struct drm_plane *plane, uint64_t iova);
};
struct phytium_plane_state {
struct drm_plane_state base;
};
#define to_phytium_plane(x) container_of(x, struct phytium_plane, base)
#define to_phytium_plane_state(x) container_of(x, struct phytium_plane_state, base)
struct phytium_plane *phytium_primary_plane_create(struct drm_device *dev, int pipe);
struct phytium_plane *phytium_cursor_plane_create(struct drm_device *dev, int pipe);
#endif /* __PHYTIUM_PLANE_H__ */

View File

@ -0,0 +1,307 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Phytium display engine DRM driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <linux/of_device.h>
#include <linux/acpi.h>
#include <drm/drm_drv.h>
#include <linux/dma-mapping.h>
#include "phytium_display_drv.h"
#include "phytium_platform.h"
#include "phytium_dp.h"
#include "phytium_gem.h"
#include "pe220x_dc.h"
#include "pe220x_dp.h"
int phytium_platform_carveout_mem_init(struct platform_device *pdev,
struct phytium_display_private *priv)
{
struct resource *res;
int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res) {
priv->pool_size = resource_size(res);
priv->pool_phys_addr = res->start;
}
if ((priv->pool_phys_addr != 0) && (priv->pool_size != 0)) {
priv->pool_virt_addr = ioremap_cache(priv->pool_phys_addr, priv->pool_size);
if (priv->pool_virt_addr == NULL) {
DRM_ERROR("failed to remap carveout mem(0x%llx)\n", priv->pool_phys_addr);
ret = -EINVAL;
goto failed_ioremap;
}
ret = phytium_memory_pool_init(&pdev->dev, priv);
if (ret)
goto failed_init_memory_pool;
priv->mem_state[PHYTIUM_MEM_SYSTEM_CARVEOUT_TOTAL] = priv->pool_size;
priv->support_memory_type = MEMORY_TYPE_SYSTEM_CARVEOUT;
priv->vram_hw_init = NULL;
} else {
DRM_DEBUG_KMS("not support carveout memory\n");
priv->mem_state[PHYTIUM_MEM_SYSTEM_CARVEOUT_TOTAL] = 0;
priv->support_memory_type = MEMORY_TYPE_SYSTEM_UNIFIED;
priv->vram_hw_init = NULL;
}
return 0;
failed_init_memory_pool:
iounmap(priv->pool_virt_addr);
failed_ioremap:
return ret;
}
void phytium_platform_carveout_mem_fini(struct platform_device *pdev,
struct phytium_display_private *priv)
{
if (priv->support_memory_type == MEMORY_TYPE_SYSTEM_CARVEOUT) {
phytium_memory_pool_fini(&pdev->dev, priv);
iounmap(priv->pool_virt_addr);
}
}
static struct phytium_display_private *
phytium_platform_private_init(struct platform_device *pdev)
{
struct drm_device *dev = dev_get_drvdata(&pdev->dev);
struct device_node *node;
struct fwnode_handle *np;
struct phytium_display_private *priv = NULL;
struct phytium_platform_private *platform_priv = NULL;
struct phytium_device_info *phytium_info = NULL;
int i = 0, ret = 0;
struct resource *res;
platform_priv = devm_kzalloc(&pdev->dev, sizeof(*platform_priv), GFP_KERNEL);
if (!platform_priv) {
DRM_ERROR("no memory to allocate for phytium_platform_private\n");
goto exit;
}
memset(platform_priv, 0, sizeof(*platform_priv));
priv = &platform_priv->base;
phytium_display_private_init(priv, dev);
if (pdev->dev.of_node) {
phytium_info = (struct phytium_device_info *)of_device_get_match_data(&pdev->dev);
if (!phytium_info) {
DRM_ERROR("failed to get dts id data(phytium_info)\n");
goto failed;
}
memcpy(&(priv->info), phytium_info, sizeof(struct phytium_device_info));
node = pdev->dev.of_node;
ret = of_property_read_u8(node, "pipe_mask", &priv->info.pipe_mask);
if (ret < 0) {
dev_err(&pdev->dev, "missing pipe_mask property from dts\n");
goto failed;
}
ret = of_property_read_u8(node, "edp_mask", &priv->info.edp_mask);
if (ret < 0) {
dev_err(&pdev->dev, "missing edp_mask property from dts\n");
goto failed;
}
} else if (has_acpi_companion(&pdev->dev)) {
phytium_info = (struct phytium_device_info *)acpi_device_get_match_data(&pdev->dev);
if (!phytium_info) {
DRM_ERROR("failed to get acpi id data(phytium_info)\n");
goto failed;
}
memcpy(&(priv->info), phytium_info, sizeof(struct phytium_device_info));
np = dev_fwnode(&(pdev->dev));
ret = fwnode_property_read_u8(np, "pipe_mask", &priv->info.pipe_mask);
if (ret < 0) {
dev_err(&pdev->dev, "missing pipe_mask property from acpi\n");
goto failed;
}
ret = fwnode_property_read_u8(np, "edp_mask", &priv->info.edp_mask);
if (ret < 0) {
dev_err(&pdev->dev, "missing edp_mask property from acpi\n");
goto failed;
}
}
priv->info.num_pipes = 0;
for_each_pipe_masked(priv, i)
priv->info.num_pipes++;
if (priv->info.num_pipes == 0) {
DRM_ERROR("num_pipes is zero, so exit init\n");
goto failed;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->regs = devm_ioremap_resource(&pdev->dev, res);
if (priv->regs == NULL) {
DRM_ERROR("ioremap fail, addr:0x%llx, size:0x%llx\n", res->start, res->end);
goto failed;
}
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0) {
dev_err(&pdev->dev, "failed to get irq\n");
goto failed;
}
if (IS_PE220X(priv)) {
priv->dc_hw_clear_msi_irq = NULL;
priv->dc_hw_fb_format_check = pe220x_dc_hw_fb_format_check;
}
return priv;
failed:
devm_kfree(&pdev->dev, platform_priv);
exit:
return NULL;
}
static void phytium_platform_private_fini(struct platform_device *pdev)
{
struct drm_device *dev = dev_get_drvdata(&pdev->dev);
struct phytium_display_private *priv = dev->dev_private;
struct phytium_platform_private *platform_priv = to_platform_priv(priv);
devm_kfree(&pdev->dev, platform_priv);
}
static int phytium_platform_probe(struct platform_device *pdev)
{
struct phytium_display_private *priv = NULL;
struct drm_device *dev = NULL;
int ret = 0;
dev = drm_dev_alloc(&phytium_display_drm_driver, &pdev->dev);
if (IS_ERR(dev)) {
DRM_ERROR("failed to allocate drm_device\n");
return PTR_ERR(dev);
}
dev_set_drvdata(&pdev->dev, dev);
dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
priv = phytium_platform_private_init(pdev);
if (priv)
dev->dev_private = priv;
else
goto failed_platform_private_init;
ret = phytium_platform_carveout_mem_init(pdev, priv);
if (ret) {
DRM_ERROR("failed to init system carveout memory\n");
goto failed_carveout_mem_init;
}
ret = drm_dev_register(dev, 0);
if (ret) {
DRM_ERROR("failed to register drm dev\n");
goto failed_register_drm;
}
phytium_dp_hpd_irq_setup(dev, true);
return 0;
failed_register_drm:
phytium_platform_carveout_mem_fini(pdev, priv);
failed_carveout_mem_init:
phytium_platform_private_fini(pdev);
failed_platform_private_init:
dev_set_drvdata(&pdev->dev, NULL);
drm_dev_put(dev);
return -1;
}
static int phytium_platform_remove(struct platform_device *pdev)
{
struct drm_device *dev = dev_get_drvdata(&pdev->dev);
struct phytium_display_private *priv = dev->dev_private;
phytium_dp_hpd_irq_setup(dev, false);
cancel_work_sync(&priv->hotplug_work);
drm_dev_unregister(dev);
phytium_platform_private_fini(pdev);
dev_set_drvdata(&pdev->dev, NULL);
drm_dev_put(dev);
return 0;
}
static void phytium_platform_shutdown(struct platform_device *pdev)
{
struct drm_device *dev = dev_get_drvdata(&pdev->dev);
struct phytium_display_private *priv = dev->dev_private;
priv->display_shutdown(dev);
}
static int phytium_platform_pm_suspend(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct phytium_display_private *priv = drm_dev->dev_private;
return priv->display_pm_suspend(drm_dev);
}
static int phytium_platform_pm_resume(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct phytium_display_private *priv = drm_dev->dev_private;
return priv->display_pm_resume(drm_dev);
}
static const struct dev_pm_ops phytium_platform_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(phytium_platform_pm_suspend, phytium_platform_pm_resume)
};
static const struct phytium_device_info pe220x_info = {
.platform_mask = BIT(PHYTIUM_PLATFORM_PE220X),
.total_pipes = 2,
.crtc_clock_max = PE220X_DC_PIX_CLOCK_MAX,
.hdisplay_max = PE220X_DC_HDISPLAY_MAX,
.vdisplay_max = PE220X_DC_VDISPLAY_MAX,
.address_mask = PE220X_DC_ADDRESS_MASK,
.backlight_max = PE220X_DP_BACKLIGHT_MAX,
};
static const struct of_device_id display_of_match[] = {
{
.compatible = "phytium,dc",
.data = &pe220x_info,
},
{ }
};
#ifdef CONFIG_ACPI
static const struct acpi_device_id display_acpi_ids[] = {
{
.id = "PHYT0015",
.driver_data = (kernel_ulong_t)&pe220x_info,
},
{},
};
MODULE_DEVICE_TABLE(acpi, display_acpi_ids);
#else
#define display_acpi_ids NULL
#endif
struct platform_driver phytium_platform_driver = {
.driver = {
.name = "phytium_display_platform",
.of_match_table = of_match_ptr(display_of_match),
.acpi_match_table = ACPI_PTR(display_acpi_ids),
},
.probe = phytium_platform_probe,
.remove = phytium_platform_remove,
.shutdown = phytium_platform_shutdown,
.driver.pm = &phytium_platform_pm_ops,
};

View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_PLATFORM_H__
#define __PHYTIUM_PLATFORM_H__
struct phytium_platform_private {
struct phytium_display_private base;
};
#define to_platform_priv(priv) container_of(priv, struct phytium_platform_private, base)
extern struct platform_driver phytium_platform_driver;
#endif /* __PHYTIUM_PLATFORM_H__ */

View File

@ -0,0 +1,365 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PHYTIUM_REG_H__
#define __PHYTIUM_REG_H__
/******************************register base******************************************/
#define PX210_PIPE_BASE(pipe) (0x8000*pipe)
#define PX210_DC_BASE(pipe) (PX210_PIPE_BASE(pipe) + 0x0000)
#define PX210_DCREQ_BASE(pipe) (PX210_PIPE_BASE(pipe) + 0x2000)
#define PX210_DP_BASE(pipe) (PX210_PIPE_BASE(pipe) + 0x3000)
#define PX210_ADDRESS_TRANSFORM_BASE 0x4000
#define PX210_PHY_ACCESS_BASE(pipe) (PX210_PIPE_BASE(pipe) + 0x5000)
#define PE220X_DC_BASE(pipe) (0x1000*pipe)
#define PE220X_DP_BASE(pipe) (0x4000 + 0x1000*pipe)
#define PE220X_ADDRESS_TRANSFORM_BASE 0x8000
#define PE220X_PHY_ACCESS_BASE(pipe) (0x6000 + 0x1000*pipe)
/******************************register base end******************************************/
/******************************dc register start******************************************/
#define PHYTIUM_DC_FRAMEBUFFER_Y_ADDRESS 0x1400
#define ADDRESS_MASK 0xffffff80
#define PHYTIUM_DC_FRAMEBUFFER_Y_STRIDE 0x1408
#define PHYTIUM_DC_PANEL_CONFIG 0x1418
#define PANEL_DATAENABLE_ENABLE (1<<0)
#define PANEL_DATA_ENABLE (1<<4)
#define PANEL_CLOCK_ENABLE (1<<8)
#define PHYTIUM_DC_HDISPLAY 0x1430
#define HDISPLAY_END_SHIFT 0
#define HDISPLAY_END_MASK 0x7fff
#define HDISPLAY_TOTAL_SHIFT 16
#define HDISPLAY_TOTAL_MASK 0x7fff
#define PHYTIUM_DC_HSYNC 0x1438
#define HSYNC_START_SHIFT 0
#define HSYNC_START_MASK 0x7fff
#define HSYNC_END_SHIFT 15
#define HSYNC_END_MASK 0x7fff
#define HSYNC_PULSE_ENABLED (1<<30)
#define HSYNC_NEGATIVE (1<<31)
#define PHYTIUM_DC_VDISPLAY 0x1440
#define VDISPLAY_END_SHIFT 0
#define VDISPLAY_END_MASK 0x7fff
#define VDISPLAY_TOTAL_SHIFT 16
#define VDISPLAY_TOTAL_MASK 0x7fff
#define PHYTIUM_DC_VSYNC 0x1448
#define VSYNC_START_SHIFT 0
#define VSYNC_START_MASK 0x7fff
#define VSYNC_END_SHIFT 15
#define VSYNC_END_MASK 0x7fff
#define VSYNC_PULSE_ENABLED (1<<30)
#define VSYNC_NEGATIVE (1<<31)
#define PHYTIUM_DC_DISPLAY_CURRENT_LOCATION 0x1450
#define PHYTIUM_DC_GAMMA_INDEX 0x1458
#define GAMMA_INDEX_MAX 256
#define PHYTIUM_DC_GAMMA_DATA 0x1460
#define GAMMA_BLUE_SHIFT 0
#define GAMMA_BLUE_MASK 0x3ff
#define GAMMA_GREEN_SHIFT 10
#define GAMMA_GREEN_MASK 0x3ff
#define GAMMA_RED_SHIFT 20
#define GAMMA_RED_MASK 0x3ff
#define PHYTIUM_DC_CURSOR_CONFIG 0x1468
#define CURSOR_FORMAT_DISABLED 0x0
#define CURSOR_FORMAT_MASKMODE 0x3
#define CURSOR_FORMAT_ARGB8888 0x2
#define CURSOR_FORMAT_MASK 0x3
#define CURSOR_HOT_Y_SHIFT 8
#define CURSOR_HOT_Y_MASK 0x1f
#define CURSOR_HOT_X_SHIFT 16
#define CURSOR_HOT_X_MASK 0x1f
#define PHYTIUM_DC_CURSOR_ADDRESS 0x146c
#define PHYTIUM_DC_CURSOR_LOCATION 0x1470
#define CURSOR_X_SHIFT 0
#define CURSOR_X_MASK 0x7fff
#define CURSOR_Y_SHIFT 16
#define CURSOR_Y_MASK 0x7fff
#define PHYTIUM_DC_CURSOR_BACKGROUND 0x1474
#define PHYTIUM_DC_CURSOR_FOREGROUND 0x1478
#define PHYTIUM_DC_INT_STATUS 0x147c
#define INT_STATUS 0x1
#define PHYTIUM_DC_INT_ENABLE 0x1480
#define INT_ENABLE 0x1
#define INT_DISABLE 0x0
#define PHYTIUM_DC_FRAMEBUFFER_CONFIG 0x1518
#define FRAMEBUFFER_OUTPUT BIT(0)
#define FRAMEBUFFER_GAMMA_ENABLE BIT(2)
#define FRAMEBUFFER_VALID_PENDING BIT(3)
#define FRAMEBUFFER_RESET BIT(4)
#define FRAMEBUFFER_PROGRESS BIT(6)
#define FRAMEBUFFER_ROT_ANGLE_SHIFT (11)
#define FRAMEBUFFER_ROT_ANGLE_MASK (0x7)
#define FRAMEBUFFER_ROT_ANGLE_ROT0 (0)
#define FRAMEBUFFER_ROT_ANGLE_FLIP_X (1)
#define FRAMEBUFFER_ROT_ANGLE_FLIP_Y (2)
#define FRAMEBUFFER_TILE_MODE_SHIFT (17)
#define FRAMEBUFFER_TILE_MODE_MASK (0x1f)
#define FRAMEBUFFER_LINEAR 0
#define FRAMEBUFFER_TILE_MODE0 4
#define FRAMEBUFFER_TILE_MODE3 7
#define FRAMEBUFFER_FORMAT_SHIFT 26
#define FRAMEBUFFER_FORMAT_MASK 0x3f
#define FRAMEBUFFER_FORMAT_XRGB4444 0x0
#define FRAMEBUFFER_FORMAT_ARGB4444 0x1
#define FRAMEBUFFER_FORMAT_XRGB1555 0x2
#define FRAMEBUFFER_FORMAT_ARGB1555 0x3
#define FRAMEBUFFER_FORMAT_RGB565 0x4
#define FRAMEBUFFER_FORMAT_XRGB8888 0x5
#define FRAMEBUFFER_FORMAT_ARGB8888 0x6
#define FRAMEBUFFER_FORMAT_YUYV 0x7
#define FRAMEBUFFER_FORMAT_UYVY 0x8
#define FRAMEBUFFER_FORMAT_NV12 0x11
#define FRAMEBUFFER_FORMAT_NV16 0x12
#define FRAMEBUFFER_FORMAT_ARGB2101010 0x16
#define FRAMEBUFFER_SWIZZLE_SHIFT 23
#define FRAMEBUFFER_SWIZZLE_MASK 0x3
#define FRAMEBUFFER_SWIZZLE_ARGB 0
#define FRAMEBUFFER_SWIZZLE_RGBA 1
#define FRAMEBUFFER_SWIZZLE_ABGR 2
#define FRAMEBUFFER_SWIZZLE_BGRA 3
#define FRAMEBUFFER_UVSWIZZLE_SHIFT 25
#define FRAMEBUFFER_UVSWIZZLE_DISABLE 0
#define FRAMEBUFFER_UVSWIZZLE_ENABLE 1
#define FRAMEBUFFER_CLEAR BIT(8)
#define FRAMEBUFFER_SCALE_ENABLE BIT(22)
#define PHYTIUM_DC_FRAMEBUFFER_SCALECONFIG 0x1520
#define FRAMEBUFFER_FILTER_TAP 3
#define FRAMEBUFFER_HORIZONTAL_FILTER_TAP 3
#define FRAMEBUFFER_TAP 0x33
#define PHYTIUM_DC_FRAMEBUFFER_U_ADDRESS 0x1530
#define PHYTIUM_DC_FRAMEBUFFER_V_ADDRESS 0x1538
#define PHYTIUM_DC_OVERLAY_CONFIG 0x1540
#define PX210_DC_OVERLAY_ENABLE BIT(24)
#define PHYTIUM_DC_FRAMEBUFFER_U_STRIDE 0x1800
#define PHYTIUM_DC_FRAMEBUFFER_V_STRIDE 0x1808
#define PHYTIUM_DC_FRAMEBUFFER_SIZE 0x1810
#define WIDTH_SHIFT 0
#define WIDTH_MASK 0x7fff
#define HEIGHT_SHIFT 15
#define HEIGHT_MASK 0x7fff
#define PHYTIUM_DC_FRAMEBUFFER_SCALE_FACTOR_X 0x1828
#define SCALE_FACTOR_X_MASK 0x7fffffff
#define PHYTIUM_DC_FRAMEBUFFER_SCALE_FACTOR_Y 0x1830
#define SCALE_FACTOR_Y_MASK 0x7fffffff
#define SCALE_FACTOR_Y_MAX 0x3
#define SCALE_FACTOR_SRC_OFFSET 16
#define PHYTIUM_DC_FRAMEBUFFER_HORI_FILTER_INDEX 0x1838
#define HORI_FILTER_INDEX 0x0
#define PHYTIUM_DC_FRAMEBUFFER_HORI_FILTER 0x1a00
#define PHYTIUM_DC_FRAMEBUFFER_VERT_FILTER_INDEX 0x1a08
#define VERT_FILTER_INDEX 0x0
#define PHYTIUM_DC_FRAMEBUFFER_VERT_FILTER 0x1a10
#define PHYTIUM_DC_FRAMEBUFFER_CLEARVALUE 0x1a18
#define CLEAR_VALUE_RED 0x00ff0000
#define CLEAR_VALUE_GREEN 0x0000ff00
#define CLEAR_VALUE_BLACK 0x00000000
#define PHYTIUM_DC_FRAMEBUFFER_INITIALOFFSET 0x1a20
#define INITIALOFFSET (0x8000 | (0X8000 << 16))
#define PHYTIUM_DC_DP_CONFIG 0x1cd0
#define OUTPUT_DP (1<<3)
#define DP_RGB666 (0x1)
#define DP_RGB888 (0x2)
#define DP_RGB101010 (0x3)
/******************************dc register end********************************************/
/******************************phy access register****************************************/
#define PHYTIUM_PHY_ACCESS_ADDRESS 0x0000
#define PHYTIUM_PHY_WRITE_DATA 0x0004
#define PHYTIUM_PHY_READ_DATA 0x0008
#define PHYTIUM_PHY_ACCESS_CTRL 0x000c
#define ACCESS_WRITE (1<<0)
#define ACCESS_READ (1<<1)
/******************************phy access register end*************************************/
/******************************dp register start******************************************/
#define PHYTIUM_DP_LINK_BW_SET 0x0000
#define PHYTIUM_DP_LANE_COUNT_SET 0x0004
#define PHYTIUM_DP_ENHANCED_FRAME_EN 0x0008
#define ENHANCED_FRAME_ENABLE 0x1
#define ENHANCED_FRAME_DISABLE 0x0
#define PHYTIUM_DP_TRAINING_PATTERN_SET 0x000c
#define TRAINING_OFF 0x0
#define TRAINING_PATTERN_1 0x1
#define TRAINING_PATTERN_2 0x2
#define TRAINING_PATTERN_3 0x3
#define TRAINING_PATTERN_4 0x4
#define PHYTIUM_DP_LINK_QUAL_PATTERN_SET 0x0010
#define TEST_PATTERN_NONE 0x0
#define TEST_PATTERN_D10_2 0x1
#define TEST_PATTERN_SYMBOL_ERROR 0x2
#define TEST_PATTERN_PRBS7 0x3
#define TEST_PATTERN_80BIT_CUSTOM 0x4
#define TEST_PATTERN_CP2520_1 0x5
#define TEST_PATTERN_CP2520_2 0x6
#define TEST_PATTERN_CP2520_3 0x7
#define TEST_PATTERN_LANE_SHIFT 8
#define PHYTIUM_DP_SCRAMBLING_DISABLE 0x0014
#define SCRAMBLING_ENABLE 0x0
#define SCRAMBLING_DISABLE 0x1
#define PHYTIUM_DP_DOWNSPREAD_CTRL 0x0018
#define PHYTIUM_DP_ALT_SCRAMBLER_RESET 0x001c
#define PHYTIUM_DP_HBR2_SCRAMBLER_RESET 0x0020
#define PHYTIUM_DP_DISPLAYPORT_VERSION 0x0024
#define PHYTIUM_DP_CUSTOM_80BIT_PATTERN_0 0x0030
#define PHYTIUM_DP_CUSTOM_80BIT_PATTERN_1 0x0034
#define PHYTIUM_DP_CUSTOM_80BIT_PATTERN_2 0x0038
#define PHYTIUM_DP_TRANSMITTER_OUTPUT_ENABLE 0x0080
#define TRANSMITTER_OUTPUT_ENABLE BIT(0)
#define TRANSMITTER_OUTPUT_DISABLE 0
#define PHYTIUM_DP_VIDEO_STREAM_ENABLE 0x0084
#define SST_MST_SOURCE_0_ENABLE BIT(0)
#define SST_MST_SOURCE_0_ENABLE_MASK 0x1
#define SST_MST_SOURCE_0_DISABLE 0
#define PHYTIUM_DP_SECONDARY_STREAM_ENABLE 0x0088
#define SECONDARY_STREAM_ENABLE 0x1
#define SECONDARY_STREAM_DISABLE 0x0
#define PHYTIUM_DP_SEC_DATA_WINDOW 0x008C
#define PHYTIUM_DP_SOFT_RESET 0x0090
#define LINK_SOFT_RESET (0x1 << 0)
#define VIDEO_SOFT_RESET (0x1 << 1)
#define PHYTIUM_INPUT_SOURCE_ENABLE 0x0094
#define VIRTUAL_SOURCE_0_ENABLE BIT(0)
#define VIRTUAL_SOURCE_0_ENABLE_MASK 0x1
#define PHYTIUM_DP_FORCE_SCRAMBLER_RESET 0x00C0
#define SCRAMBLER_RESET BIT(0)
#define PHYTIUM_DP_SOURCE_CONTROL_STATUS 0x00C4
#define PHYTIUM_DP_DATA_CONTROL 0x00C8
#define PHYTIUM_DP_CORE_CAPABILITY 0x00F8
#define PHYTIUM_DP_CORE_ID 0x00FC
#define PHYTIUM_DP_AUX_COMMAND 0x0100
#define BYTE_COUNT_MASK 0xf
#define COMMAND_SHIFT 8
#define COMMAND_MASK 0xf
#define ADDRESS_ONLY (1<<12)
#define PHYTIUM_DP_AUX_WRITE_FIFO 0x0104
#define PHYTIUM_DP_AUX_ADDRESS 0x0108
#define PHYTIUM_DP_AUX_CLK_DIVIDER 0x010C
#define AUX_CLK_DIVIDER 48
#define AUX_CLK_DIVIDER_100 100
#define PHYTIUM_DP_SINK_HPD_STATE 0x0128
#define HPD_CONNECT 0x1
#define HPD_DISCONNECT 0x0
#define PHYTIUM_DP_INTERRUPT_RAW_STATUS 0x0130
#define REPLY_TIMEOUT (1<<3)
#define DP_STATUS_REQUEST_IN_PROGRESS (1<<1)
#define HPD_STATE (0<<1)
#define PHYTIUM_DP_AUX_REPLY_DATA 0x0134
#define PHYTIUM_DP_AUX_REPLY_CODE 0x0138
#define AUX_NATIVE_ACK (0x0<<0)
#define AUX_NATIVE_NACK (0x1<<0)
#define AUX_NATIVE_DEFER (0x2<<0)
#define AUX_NATIVE_MASK (0x3 << 0)
#define AUX_I2C_ACK (0x0<<2)
#define AUX_I2C_NACK (0x1<<2)
#define AUX_I2C_DEFER (0x2<<2)
#define AUX_I2C_MASK (0x3 << 2)
#define PHYTIUM_DP_INTERRUPT_STATUS 0x0140
#define HPD_IRQ (1<<1)
#define HPD_EVENT (1<<0)
#define PHYTIUM_DP_INTERRUPT_MASK 0x0144
#define HPD_IRQ_MASK (1<<1)
#define HPD_EVENT_MASK (1<<0)
#define HPD_OTHER_MASK 0x3c
#define PHYTIUM_DP_AUX_REPLY_DATA_COUNT 0x0148
#define PHYTIUM_DP_AUX_STATUS 0x014C
#define REPLY_RECEIVED 0x1
#define REPLY_IN_PROGRESS 0x2
#define REQUEST_IN_PROGRESS 0x4
#define REPLY_ERROR 0x8
#define PHYTIUM_DP_AUX_TIMER 0x0158
#define PHYTIUM_DP_MAIN_LINK_HTOTAL 0x0180
#define PHYTIUM_DP_MAIN_LINK_VTOTAL 0x0184
#define PHYTIUM_DP_MAIN_LINK_POLARITY 0x0188
#define VSYNC_POLARITY_LOW BIT(1)
#define HSYNC_POLARITY_LOW BIT(0)
#define PHYTIUM_DP_MAIN_LINK_HSWIDTH 0x018C
#define PHYTIUM_DP_MAIN_LINK_VSWIDTH 0x0190
#define PHYTIUM_DP_MAIN_LINK_HRES 0x0194
#define PHYTIUM_DP_MAIN_LINK_VRES 0x0198
#define PHYTIUM_DP_MAIN_LINK_HSTART 0x019C
#define PHYTIUM_DP_MAIN_LINK_VSTART 0x01A0
#define PHYTIUM_DP_MAIN_LINK_MISC0 0x01A4
#define MISC0_SYNCHRONOUS_CLOCK BIT(0)
#define MISC0_BIT_DEPTH_OFFSET 5
#define MISC0_BIT_DEPTH_6BIT 0x0
#define MISC0_BIT_DEPTH_8BIT 0x1
#define MISC0_BIT_DEPTH_10BIT 0x2
#define MISC0_COMPONENT_FORMAT_SHIFT 1
#define MISC0_COMPONENT_FORMAT_RGB 0x0
#define PHYTIUM_DP_MAIN_LINK_MISC1 0x01A8
#define PHYTIUM_DP_M_VID 0x01AC
#define PHYTIUM_DP_TRANSFER_UNIT_SIZE 0x01B0
#define PHYTIUM_DP_N_VID 0x01B4
#define PHYTIUM_DP_USER_PIXEL_WIDTH 0x01B8
#define PHYTIUM_DP_DATA_COUNT 0x01BC
#define PHYTIUM_DP_INTERLACED 0x01C0
#define PHYTIUM_DP_USER_SYNC_POLARITY 0x01C4
#define USER_ODDEVEN_POLARITY_HIGH BIT(3)
#define USER_DATA_ENABLE_POLARITY_HIGH BIT(2)
#define USER_VSYNC_POLARITY_HIGH BIT(1)
#define USER_HSYNC_POLARITY_HIGH BIT(0)
#define PHYTIUM_DP_USER_CONTROL 0x01C8
#define PHYTIUM_EDP_CRC_ENABLE 0x01D0
#define SUPPORT_EDP_1_4 BIT(1)
#define PHYTIUM_EDP_CRC_RED 0x01D4
#define PHYTIUM_EDP_CRC_GREEN 0x01D8
#define PHYTIUM_EDP_CRC_BLUE 0x01DC
#define PHYTIUM_DP_SEC_AUDIO_ENABLE 0x0300
#define SEC_AUDIO_ENABLE BIT(0)
#define CHANNEL_MUTE_ENABLE BIT(1)
#define PHYTIUM_DP_SEC_INPUT_SELECT 0x0304
#define INPUT_SELECT_I2S 0x0
#define PHYTIUM_DP_SEC_CHANNEL_COUNT 0x0308
#define CHANNEL_2 0x2
#define CHANNEL_2_LFE 0x3
#define CHANNEL_5_1 0x6
#define CHANNEL_7_1 0x7
#define CHANNEL_MASK 0xf
#define PHYTIUM_DP_SEC_DIRECT_CLKDIV 0x030c
#define APB_CLOCK 48000000
#define PHYTIUM_DP_SEC_MAUD 0x0318
#define PHYTIUM_DP_SEC_NAUD 0x031c
#define PHYTIUM_DP_SEC_CLOCK_MODE 0x0320
#define CLOCK_MODE_SYNC 0x1
#define PHYTIUM_DP_SEC_CS_SOURCE_FORMAT 0x0340
#define CS_SOURCE_FORMAT_DEFAULT 0x0
#define PHYTIUM_DP_SEC_CS_CATEGORY_CODE 0x0344
#define PHYTIUM_DP_SEC_CS_LENGTH_ORIG_FREQ 0x0348
#define ORIG_FREQ_32000 0xc
#define ORIG_FREQ_44100 0xf
#define ORIG_FREQ_48000 0xd
#define ORIG_FREQ_88200 0x7
#define ORIG_FREQ_96000 0x5
#define ORIG_FREQ_176400 0x3
#define ORIG_FREQ_192000 0x1
#define ORIG_FREQ_MASK 0xf
#define ORIG_FREQ_SHIFT 0
#define WORD_LENGTH_16 0x4
#define WORD_LENGTH_18 0x2
#define WORD_LENGTH_20 0xc
#define WORD_LENGTH_24 0xd
#define WORD_LENGTH_MASK 0xf
#define WORD_LENGTH_SHIFT 4
#define PHYTIUM_DP_SEC_CS_FREQ_CLOCK_ACCURACY 0x034c // not used
#define SAMPLING_FREQ_32000 0xc
#define SAMPLING_FREQ_44100 0x0
#define SAMPLING_FREQ_48000 0x4
#define SAMPLING_FREQ_88200 0x1
#define SAMPLING_FREQ_96000 0x5
#define SAMPLING_FREQ_176400 0x3
#define SAMPLING_FREQ_192000 0x7
#define SAMPLING_FREQ_MASK 0xf
#define SAMPLING_FREQ_SHIFT 4
#define PHYTIUM_DP_SEC_CHANNEL_MAP 0x035C
#define CHANNEL_MAP_DEFAULT 0x87654321
/******************************dp register end********************************************/
#endif /* __PHYTIUM_REG_H__ */

View File

@ -0,0 +1,326 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display 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 "px210_reg.h"
#include "phytium_crtc.h"
#include "phytium_plane.h"
#include "phytium_fb.h"
#include "phytium_gem.h"
static const unsigned int px210_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,
};
static uint64_t px210_primary_formats_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_PHYTIUM_TILE_MODE0_FBCDC,
DRM_FORMAT_MOD_PHYTIUM_TILE_MODE3_FBCDC,
DRM_FORMAT_MOD_INVALID
};
static uint64_t px210_cursor_formats_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
};
static const unsigned int px210_cursor_formats[] = {
DRM_FORMAT_ARGB8888,
};
void px210_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;
config = phytium_readl_reg(priv, group_offset,
PX210_GPU_ADDRESS_TRANSFORM_SRC_ADDR);
if (config)
phytium_writel_reg(priv, config, group_offset,
PX210_GPU_ADDRESS_TRANSFORM_SRC_ADDR);
config = phytium_readl_reg(priv, group_offset,
PX210_GPU_ADDRESS_TRANSFORM_SIZE);
if (config)
phytium_writel_reg(priv, config, group_offset,
PX210_GPU_ADDRESS_TRANSFORM_SIZE);
config = phytium_readl_reg(priv, group_offset,
PX210_GPU_ADDRESS_TRANSFORM_DST_ADDR);
if (config)
phytium_writel_reg(priv, config, group_offset,
PX210_GPU_ADDRESS_TRANSFORM_DST_ADDR);
phytium_writel_reg(priv, (vram_addr & SRC_ADDR_MASK) >> SRC_ADDR_OFFSET,
group_offset, PX210_DC_ADDRESS_TRANSFORM_SRC_ADDR);
phytium_writel_reg(priv, (vram_size >> SIZE_OFFSET) | ADDRESS_TRANSFORM_ENABLE,
group_offset, PX210_DC_ADDRESS_TRANSFORM_SIZE);
config = phytium_readl_reg(priv, group_offset, PX210_DC_ADDRESS_TRANSFORM_DST_ADDR);
phytium_writel_reg(priv, config, group_offset, PX210_DC_ADDRESS_TRANSFORM_DST_ADDR);
}
void px210_dc_hw_clear_msi_irq(struct phytium_display_private *priv, uint32_t phys_pipe)
{
phytium_writel_reg(priv, MSI_CLEAR, priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_MSI_CLEAR);
}
void px210_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;
uint32_t group_offset = priv->dcreq_reg_base[phys_pipe];
int ret = 0;
/* config pix clock */
phytium_writel_reg(priv, FLAG_REQUEST | CMD_PIXEL_CLOCK | (clock & PIXEL_CLOCK_MASK),
group_offset, PX210_DCREQ_CMD_REGISTER);
ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER,
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to set pixel clock\n", __func__);
}
void px210_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 reset_timeout = 100;
int config = 0;
int phys_pipe = phytium_crtc->phys_pipe;
// reset dc
config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_CONTROL);
phytium_writel_reg(priv, config | SOFT_RESET, priv->dc_reg_base[phys_pipe],
PX210_DC_CLOCK_CONTROL);
phytium_writel_reg(priv, 0, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_CONTROL);
do {
config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_IDLE);
if (config | IS_IDLE)
break;
mdelay(1);
reset_timeout--;
} while (reset_timeout);
/* reset pix clock */
px210_dc_hw_config_pix_clock(crtc, 0);
// reset dc
reset_timeout = 100;
config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_CONTROL);
phytium_writel_reg(priv, config | SOFT_RESET, priv->dc_reg_base[phys_pipe],
PX210_DC_CLOCK_CONTROL);
phytium_writel_reg(priv, 0, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_CONTROL);
do {
config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_IDLE);
if (config | IS_IDLE)
break;
mdelay(1);
reset_timeout--;
} while (reset_timeout);
/* reset dcreq */
phytium_writel_reg(priv, DCREQ_PLAN_A, priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_PLAN);
phytium_writel_reg(priv, 0, priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_CONTROL);
phytium_writel_reg(priv, DCREQ_RESET, priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_RESET);
msleep(20);
phytium_writel_reg(priv, (~DCREQ_RESET)&DCREQ_RESET_MASK,
priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_RESET);
}
int px210_dc_hw_fb_format_check(const struct drm_mode_fb_cmd2 *mode_cmd, int count)
{
int ret = 0;
switch (mode_cmd->modifier[count]) {
case DRM_FORMAT_MOD_PHYTIUM_TILE_MODE0_FBCDC:
switch (mode_cmd->pixel_format) {
case DRM_FORMAT_ARGB4444:
case DRM_FORMAT_ABGR4444:
case DRM_FORMAT_RGBA4444:
case DRM_FORMAT_BGRA4444:
case DRM_FORMAT_XRGB4444:
case DRM_FORMAT_XBGR4444:
case DRM_FORMAT_RGBX4444:
case DRM_FORMAT_BGRX4444:
case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_ABGR1555:
case DRM_FORMAT_RGBA5551:
case DRM_FORMAT_BGRA5551:
case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_XBGR1555:
case DRM_FORMAT_RGBX5551:
case DRM_FORMAT_BGRX5551:
case DRM_FORMAT_RGB565:
case DRM_FORMAT_BGR565:
case DRM_FORMAT_YUYV:
case DRM_FORMAT_UYVY:
break;
default:
DRM_ERROR("TILE_MODE0_FBCDC not support DRM_FORMAT %d",
mode_cmd->pixel_format);
ret = -EINVAL;
goto error;
}
break;
case DRM_FORMAT_MOD_PHYTIUM_TILE_MODE3_FBCDC:
switch (mode_cmd->pixel_format) {
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_BGRA1010102:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_BGRX8888:
break;
default:
DRM_ERROR("TILE_MODE3_FBCDC not support DRM_FORMAT %d",
mode_cmd->pixel_format);
ret = -EINVAL;
goto error;
}
break;
case DRM_FORMAT_MOD_LINEAR:
break;
default:
DRM_ERROR("unsupported fb modifier 0x%llx\n", mode_cmd->modifier[0]);
ret = -EINVAL;
goto error;
}
return 0;
error:
return ret;
}
void px210_dc_hw_plane_get_primary_format(const uint64_t **format_modifiers,
const uint32_t **formats,
uint32_t *format_count)
{
*format_modifiers = px210_primary_formats_modifiers;
*formats = px210_primary_formats;
*format_count = ARRAY_SIZE(px210_primary_formats);
}
void px210_dc_hw_plane_get_cursor_format(const uint64_t **format_modifiers,
const uint32_t **formats,
uint32_t *format_count)
{
*format_modifiers = px210_cursor_formats_modifiers;
*formats = px210_cursor_formats;
*format_count = ARRAY_SIZE(px210_cursor_formats);
}
void px210_dc_hw_update_dcreq(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;
uint32_t group_offset = priv->dcreq_reg_base[phys_pipe];
int config;
if (phytium_plane->tiling[0] == FRAMEBUFFER_LINEAR) {
phytium_writel_reg(priv, DCREQ_MODE_LINEAR,
group_offset, PX210_DCREQ_PLANE0_CONFIG);
} else {
config = DCREQ_NO_LOSSY;
if (phytium_plane->tiling[0] == FRAMEBUFFER_TILE_MODE0)
config |= DCREQ_TILE_TYPE_MODE0;
else if (phytium_plane->tiling[0] == FRAMEBUFFER_TILE_MODE3)
config |= DCREQ_TILE_TYPE_MODE3;
else
config |= DCREQ_TILE_TYPE_MODE0;
switch (phytium_plane->format) {
case FRAMEBUFFER_FORMAT_ARGB8888:
case FRAMEBUFFER_FORMAT_XRGB8888:
config |= DCREQ_COLOURFORMAT_BGRA8888;
break;
case FRAMEBUFFER_FORMAT_ARGB2101010:
config |= DCREQ_COLOURFORMAT_ARGB2101010;
break;
case FRAMEBUFFER_FORMAT_XRGB4444:
case FRAMEBUFFER_FORMAT_ARGB4444:
config |= DCREQ_COLOURFORMAT_ARGB4444;
break;
case FRAMEBUFFER_FORMAT_XRGB1555:
case FRAMEBUFFER_FORMAT_ARGB1555:
config |= DCREQ_COLOURFORMAT_ARGB1555;
break;
case FRAMEBUFFER_FORMAT_RGB565:
config |= DCREQ_COLOURFORMAT_RGB565;
break;
case FRAMEBUFFER_FORMAT_YUYV:
config |= DCREQ_COLOURFORMAT_YUYV;
break;
case FRAMEBUFFER_FORMAT_UYVY:
config |= DCREQ_COLOURFORMAT_UYVY;
break;
}
config |= DCREQ_ARGBSWIZZLE_ARGB;
config |= DCREQ_MODE_TILE;
phytium_writel_reg(priv, phytium_plane->iova[0] & 0xffffffff,
group_offset, PX210_DCREQ_PLANE0_ADDR_START);
phytium_writel_reg(priv, (phytium_plane->iova[0] + phytium_plane->size[0]) &
0xffffffff, group_offset, PX210_DCREQ_PLANE0_ADDR_END);
phytium_writel_reg(priv, config, group_offset, PX210_DCREQ_PLANE0_CONFIG);
}
}
void px210_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->dcreq_reg_base[phys_pipe], PX210_DCREQ_PIX_DMA_PREFIX);
}

View File

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PX210_DC_H__
#define __PX210_DC_H__
#define PX210_DC_PIX_CLOCK_MAX (594000)
#define PX210_DC_HDISPLAY_MAX 3840
#define PX210_DC_VDISPLAY_MAX 2160
#define PX210_DC_ADDRESS_MASK 0x7f
extern void px210_dc_hw_vram_init(struct phytium_display_private *priv,
resource_size_t vram_addr,
resource_size_t vram_size);
extern void px210_dc_hw_clear_msi_irq(struct phytium_display_private *priv, uint32_t phys_pipe);
extern void px210_dc_hw_config_pix_clock(struct drm_crtc *crtc, int clock);
extern void px210_dc_hw_disable(struct drm_crtc *crtc);
extern int px210_dc_hw_fb_format_check(const struct drm_mode_fb_cmd2 *mode_cmd, int count);
extern void px210_dc_hw_plane_get_primary_format(const uint64_t **format_modifiers,
const uint32_t **formats,
uint32_t *format_count);
extern void px210_dc_hw_plane_get_cursor_format(const uint64_t **format_modifiers,
const uint32_t **formats,
uint32_t *format_count);
void px210_dc_hw_update_dcreq(struct drm_plane *plane);
void px210_dc_hw_update_primary_hi_addr(struct drm_plane *plane);
#endif /* __PX210_DC_H__ */

View File

@ -0,0 +1,920 @@
// SPDX-License-Identifier: GPL-2.0
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include "phytium_display_drv.h"
#include "px210_reg.h"
#include "phytium_dp.h"
#include "px210_dp.h"
static uint8_t px210_dp_source_lane_count[3] = {4, 4, 1};
/* [reg][ling_rate 1.62->8.1] */
static int vco_val[12][4] = {
{0x0509, 0x0509, 0x0509, 0x0509}, // CP_PADJ
{0x0f00, 0x0f00, 0x0f00, 0x0f00}, // CP_IADJ
{0x0F08, 0x0F08, 0x0F08, 0x0F08}, // FILT_PADJ
{0x0061, 0x006C, 0x006C, 0x0051}, // INTDIV
{0x3333, 0x0000, 0x0000, 0x0000}, // FRACDIVL
{0x0000, 0x0000, 0x0000, 0x0000}, // FRACDIVH
{0x0042, 0x0048, 0x0048, 0x0036}, // HIGH_THR
{0x0002, 0x0002, 0x0002, 0x0002}, // PDIAG_CTRL
{0x0c5e, 0x0c5e, 0x0c5e, 0x0c5e}, // VCOCAL_PLLCNT_START
{0x00c7, 0x00c7, 0x00c7, 0x00c7}, // LOCK_PEFCNT
{0x00c7, 0x00c7, 0x00c7, 0x00c7}, // LOCK_PLLCNT_START
{0x0005, 0x0005, 0x0005, 0x0005}, // LOCK_PLLCNT_THR
};
static int mgnfs_val[4][4][4] = // [link_rate][swing][emphasis]
{
/* 1.62Gbps */
{
{0x0026, 0x001f, 0x0012, 0x0000},
{0x0013, 0x0013, 0x0000, 0x0000},
{0x0006, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 2.7Gbps */
{
{0x0026, 0x001f, 0x0012, 0x0000},
{0x0013, 0x0013, 0x0000, 0x0000},
{0x0006, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 5.4Gbps */
{
{0x0026, 0x0013, 0x005, 0x0000},
{0x0018, 0x006, 0x0000, 0x0000},
{0x000c, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 8.1Gbps */
{
{0x0026, 0x0013, 0x005, 0x0000},
{0x0013, 0x006, 0x0000, 0x0000},
{0x0006, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
};
static int cpost_val[4][4][4] = // [link_rate][swing][emphasis]
{
/* 1.62Gbps */
{
{0x0000, 0x0014, 0x0020, 0x002a},
{0x0000, 0x0010, 0x001f, 0x0000},
{0x0000, 0x0013, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 2.7Gbps */
{
{0x0000, 0x0014, 0x0020, 0x002a},
{0x0000, 0x0010, 0x001f, 0x0000},
{0x0000, 0x0013, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 5.4Gbps */
{
{0x0000, 0x0014, 0x0022, 0x002e},
{0x0000, 0x0013, 0x0020, 0x0000},
{0x0000, 0x0013, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
/* 8.1Gbps */
{
{0x0000, 0x0014, 0x0022, 0x002e},
{0x0000, 0x0013, 0x0020, 0x0000},
{0x0000, 0x0013, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000},
},
};
static int px210_dp_hw_set_phy_lane_and_rate(struct phytium_dp_device *phytium_dp,
uint8_t link_lane_count,
uint32_t link_rate)
{
int port = phytium_dp->port%3;
int i = 0, data, tmp, tmp1, index = 0, mask;
int timeout = 500, ret = 0;
if (port == 0 || port == 1) {
/* set pma powerdown */
data = 0;
mask = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++) {
data |= (A3_POWERDOWN3 << i*A3_POWERDOWN3_SHIFT);
mask |= (((1<<A3_POWERDOWN3_SHIFT) - 1) << (i*A3_POWERDOWN3_SHIFT));
}
if (port == 0) {
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA0_POWER);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_PMA0_POWER, tmp);
} else {
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA1_POWER);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_PMA1_POWER, tmp);
}
/* lane pll disable */
data = 0;
mask = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++) {
data |= (PLL_EN << i*PLL_EN_SHIFT);
mask |= (((1<<PLL_EN_SHIFT) - 1) << (i*PLL_EN_SHIFT));
}
mask = (mask << (port*PLL_EN_SHIFT*4));
data = (data << (port*PLL_EN_SHIFT*4));
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PLL_EN);
tmp = (tmp & (~mask));
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL_EN, tmp);
/* pma pll disable */
mask = (CONTROL_ENABLE << (port*CONTROL_ENABLE_SHIFT));
data = (CONTROL_ENABLE << (port*CONTROL_ENABLE_SHIFT));
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA_CONTROL);
tmp = (tmp & (~mask));
phytium_phy_writel(phytium_dp, PX210_PHY0_PMA_CONTROL, tmp);
/* read pma pll disable state */
mdelay(2);
phytium_phy_readl(phytium_dp, PX210_PHY0_PMA_CONTROL2);
/* config link rate */
switch (link_rate) {
case 810000:
tmp = PLL_LINK_RATE_810000;
tmp1 = HSCLK_LINK_RATE_810000;
index = 3;
break;
case 540000:
tmp = PLL_LINK_RATE_540000;
tmp1 = HSCLK_LINK_RATE_540000;
index = 2;
break;
case 270000:
tmp = PLL_LINK_RATE_270000;
tmp1 = HSCLK_LINK_RATE_270000;
index = 1;
break;
case 162000:
tmp = PLL_LINK_RATE_162000;
tmp1 = HSCLK_LINK_RATE_162000;
index = 0;
break;
default:
DRM_ERROR("phytium dp rate(%d) not support\n", link_rate);
tmp = PLL_LINK_RATE_162000;
tmp1 = HSCLK_LINK_RATE_162000;
index = 0;
break;
}
if (port == 0) {
/* config analog pll for link0 */
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_CLK_SEL, tmp);
phytium_phy_writel(phytium_dp, PX210_PHY0_HSCLK0_SEL, HSCLK_LINK_0);
phytium_phy_writel(phytium_dp, PX210_PHY0_HSCLK0_DIV, tmp1);
/* config digital pll for link0 */
phytium_phy_writel(phytium_dp, PX210_PHY0_PLLDRC0_CTRL, PLLDRC_LINK0);
/* common for all rate */
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_DSM_M0, PLL0_DSM_M0);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_VCOCAL_START,
PLL0_VCOCAL_START);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_VCOCAL_CTRL,
PLL0_VCOCAL_CTRL);
/* different for all rate */
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_CP_PADJ,
vco_val[0][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_CP_IADJ,
vco_val[1][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_CP_FILT_PADJ,
vco_val[2][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_INTDIV,
vco_val[3][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_FRACDIVL,
vco_val[4][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_FRACDIVH,
vco_val[5][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_HIGH_THR,
vco_val[6][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_PDIAG_CTRL,
vco_val[7][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_VCOCAL_PLLCNT_START,
vco_val[8][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_LOCK_PEFCNT,
vco_val[9][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_LOCK_PLLCNT_START,
vco_val[10][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_LOCK_PLLCNT_THR,
vco_val[11][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_PSC_A0,
PLL0_TX_PSC_A0);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_PSC_A2,
PLL0_TX_PSC_A2);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_PSC_A3,
PLL0_TX_PSC_A3);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_RX_PSC_A0,
PLL0_RX_PSC_A0);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_RX_PSC_A2,
PLL0_RX_PSC_A2);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_RX_PSC_A3,
PLL0_RX_PSC_A3);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_RX_PSC_CAL,
PLL0_RX_PSC_CAL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_XCVR_CTRL,
PLL0_XCVR_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_RX_GCSM1_CTRL,
PLL0_RX_GCSM1_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_RX_GCSM2_CTRL,
PLL0_RX_GCSM2_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_RX_PERGCSM_CTRL,
PLL0_RX_PERGCSM_CTRL);
} else if (port == 1) {
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_CLK_SEL, tmp);
phytium_phy_writel(phytium_dp, PX210_PHY0_HSCLK1_SEL, HSCLK_LINK_1);
phytium_phy_writel(phytium_dp, PX210_PHY0_HSCLK1_DIV, tmp1);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLLDRC1_CTRL, PLLDRC_LINK1);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_DSM_M0, PLL1_DSM_M0);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_VCOCAL_START,
PLL1_VCOCAL_START);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_VCOCAL_CTRL,
PLL1_VCOCAL_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_CP_PADJ,
vco_val[0][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_CP_IADJ,
vco_val[1][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_CP_FILT_PADJ,
vco_val[2][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_INTDIV,
vco_val[3][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_FRACDIVL,
vco_val[4][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_FRACDIVH,
vco_val[5][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_HIGH_THR,
vco_val[6][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_PDIAG_CTRL,
vco_val[7][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_VCOCAL_PLLCNT_START,
vco_val[8][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_LOCK_PEFCNT,
vco_val[9][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_LOCK_PLLCNT_START,
vco_val[10][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_LOCK_PLLCNT_THR,
vco_val[11][index]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_PSC_A0,
PLL1_TX_PSC_A0);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_PSC_A2,
PLL1_TX_PSC_A2);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_PSC_A3,
PLL1_TX_PSC_A3);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_RX_PSC_A0,
PLL1_RX_PSC_A0);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_RX_PSC_A2,
PLL1_RX_PSC_A2);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_RX_PSC_A3,
PLL1_RX_PSC_A3);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_RX_PSC_CAL,
PLL1_RX_PSC_CAL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_XCVR_CTRL,
PLL1_XCVR_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_RX_GCSM1_CTRL,
PLL1_RX_GCSM1_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_RX_GCSM2_CTRL,
PLL1_RX_GCSM2_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_RX_PERGCSM_CTRL,
PLL1_RX_PERGCSM_CTRL);
}
/* pma pll enable */
data = 0;
mask = 0;
mask = (CONTROL_ENABLE << (port*CONTROL_ENABLE_SHIFT));
data = (CONTROL_ENABLE << (port*CONTROL_ENABLE_SHIFT));
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA_CONTROL);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_PMA_CONTROL, tmp);
/* lane pll enable */
data = 0;
mask = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++) {
data |= (PLL_EN << i*PLL_EN_SHIFT);
mask |= (((1<<PLL_EN_SHIFT) - 1) << (i*PLL_EN_SHIFT));
}
mask = (mask << (port*PLL_EN_SHIFT*4));
data = (data << (port*PLL_EN_SHIFT*4));
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PLL_EN);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL_EN, tmp);
/* set pma power active */
data = 0;
mask = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++) {
data |= (A0_ACTIVE << i*A0_ACTIVE_SHIFT);
mask |= (((1<<A0_ACTIVE_SHIFT) - 1) << (i*A0_ACTIVE_SHIFT));
}
if (port == 0) {
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA0_POWER);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_PMA0_POWER, tmp);
mask = PLL0_LOCK_DONE;
} else {
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA1_POWER);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_PMA1_POWER, tmp);
mask = PLL1_LOCK_DONE;
}
do {
mdelay(1);
timeout--;
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA_CONTROL2);
} while ((!(tmp & mask)) && timeout);
if (timeout == 0) {
DRM_ERROR("dp(%d) phy pll lock failed\n", port);
ret = -1;
}
udelay(1);
} else {
/* set pma powerdown */
mask = PHY1_A3_POWERDOWN3_MASK << PHY1_A3_POWERDOWN3_SHIFT;
data = PHY1_A3_POWERDOWN3 << PHY1_A3_POWERDOWN3_SHIFT;
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_PMA_MISC);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY1_PMA_MISC, tmp);
/* lane pll disable */
mask = PHY1_PLL_EN_MASK << PHY1_PLL_EN_SHIFT;
data = PHY1_PLL_EN << PHY1_PLL_EN_SHIFT;
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_PMA_MISC);
tmp = (tmp & (~mask));
phytium_phy_writel(phytium_dp, PX210_PHY1_PMA_MISC, tmp);
/* pma pll disable */
mask = (CONTROL_ENABLE << 0*CONTROL_ENABLE_SHIFT); // link config
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_PMA_CONTROL);
tmp = (tmp & (~mask));
phytium_phy_writel(phytium_dp, PX210_PHY1_PMA_CONTROL, tmp);
/* read pma pll disable state */
mdelay(2);
phytium_phy_readl(phytium_dp, PX210_PHY1_PMA_CONTROL2);
/* config link rate */
switch (link_rate) {
case 810000:
tmp = PLL_LINK_RATE_810000;
tmp1 = HSCLK_LINK_RATE_810000;
index = 3;
break;
case 540000:
tmp = PLL_LINK_RATE_540000;
tmp1 = HSCLK_LINK_RATE_540000;
index = 2;
break;
case 270000:
tmp = PLL_LINK_RATE_270000;
tmp1 = HSCLK_LINK_RATE_270000;
index = 1;
break;
case 162000:
tmp = PLL_LINK_RATE_162000;
tmp1 = HSCLK_LINK_RATE_162000;
index = 0;
break;
default:
DRM_ERROR("phytium dp rate(%d) not support\n", link_rate);
tmp = PLL_LINK_RATE_162000;
tmp1 = HSCLK_LINK_RATE_162000;
index = 0;
break;
}
/* config analog pll for link0 */
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL_CLK_SEL, tmp);
phytium_phy_writel(phytium_dp, PX210_PHY1_HSCLK_SEL, HSCLK_LINK_0);
phytium_phy_writel(phytium_dp, PX210_PHY1_HSCLK_DIV, tmp1);
/* config digital pll for link0 */
phytium_phy_writel(phytium_dp, PX210_PHY1_PLLDRC_CTRL, PLLDRC_LINK0);
/* common for all rate */
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_DSM_M0, PLL0_DSM_M0);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_VCOCAL_START, PLL0_VCOCAL_START);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_VCOCAL_CTRL, PLL0_VCOCAL_CTRL);
/* different for all rate */
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_CP_PADJ, vco_val[0][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_CP_IADJ, vco_val[1][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_CP_FILT_PADJ, vco_val[2][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_INTDIV, vco_val[3][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_FRACDIVL, vco_val[4][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_FRACDIVH, vco_val[5][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_HIGH_THR, vco_val[6][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_PDIAG_CTRL, vco_val[7][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_VCOCAL_PLLCNT_START,
vco_val[8][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_LOCK_PEFCNT, vco_val[9][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_LOCK_PLLCNT_START,
vco_val[10][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_LOCK_PLLCNT_THR,
vco_val[11][index]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_PSC_A0, PLL0_TX_PSC_A0);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_PSC_A2, PLL0_TX_PSC_A2);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_PSC_A3, PLL0_TX_PSC_A3);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_RX_PSC_A0, PLL0_RX_PSC_A0);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_RX_PSC_A2, PLL0_RX_PSC_A2);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_RX_PSC_A3, PLL0_RX_PSC_A3);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_RX_PSC_CAL, PLL0_RX_PSC_CAL);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_XCVR_CTRL, PLL0_XCVR_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_RX_GCSM1_CTRL,
PLL0_RX_GCSM1_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_RX_GCSM2_CTRL,
PLL0_RX_GCSM2_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_RX_PERGCSM_CTRL,
PLL0_RX_PERGCSM_CTRL);
/* pma pll enable */
data = 0;
mask = 0;
mask = (CONTROL_ENABLE << (0*CONTROL_ENABLE_SHIFT));
data = (CONTROL_ENABLE << (0*CONTROL_ENABLE_SHIFT));
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_PMA_CONTROL);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY1_PMA_CONTROL, tmp);
/* lane pll enable */
mask = PHY1_PLL_EN_MASK << PHY1_PLL_EN_SHIFT;
data = PHY1_PLL_EN << PHY1_PLL_EN_SHIFT;
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_PMA_MISC);
tmp = ((tmp & (~mask)) | data);
phytium_phy_writel(phytium_dp, PX210_PHY1_PMA_MISC, tmp);
/* set pma power active */
mask = PHY1_A0_ACTIVE_MASK << PHY1_A0_ACTIVE_SHIFT;
data = PHY1_A0_ACTIVE << PHY1_A0_ACTIVE_SHIFT;
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_PMA_MISC);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY1_PMA_MISC, tmp);
mask = PLL0_LOCK_DONE;
do {
mdelay(1);
timeout--;
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_PMA_CONTROL2);
} while ((!(tmp & mask)) && timeout);
if (timeout == 0) {
DRM_ERROR("dp(%d) phy pll lock failed\n", port);
ret = -1;
}
udelay(1);
}
return ret;
}
static void px210_dp_hw_set_phy_lane_setting(struct phytium_dp_device *phytium_dp,
uint32_t link_rate,
uint8_t train_set)
{
int port = phytium_dp->port%3;
int voltage_swing = 0;
int pre_emphasis = 0, link_rate_index = 0;
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
default:
voltage_swing = 0;
break;
case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
voltage_swing = 1;
break;
case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
voltage_swing = 2;
break;
case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
voltage_swing = 3;
break;
}
switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
case DP_TRAIN_PRE_EMPH_LEVEL_0:
default:
pre_emphasis = 0;
break;
case DP_TRAIN_PRE_EMPH_LEVEL_1:
pre_emphasis = 1;
break;
case DP_TRAIN_PRE_EMPH_LEVEL_2:
pre_emphasis = 2;
break;
case DP_TRAIN_PRE_EMPH_LEVEL_3:
pre_emphasis = 3;
break;
}
switch (link_rate) {
case 810000:
link_rate_index = 3;
break;
case 540000:
link_rate_index = 2;
break;
case 270000:
link_rate_index = 1;
break;
case 162000:
link_rate_index = 0;
break;
default:
DRM_ERROR("phytium dp rate(%d) not support\n", link_rate);
link_rate_index = 2;
break;
}
if (port == 0) {
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_DIAG_ACYA, LOCK);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_TXCC_CTRL, TX_TXCC_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_DRV, TX_DRV);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_MGNFS,
mgnfs_val[link_rate_index][voltage_swing][pre_emphasis]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_CPOST,
cpost_val[link_rate_index][voltage_swing][pre_emphasis]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_DIAG_ACYA, UNLOCK);
} else if (port == 1) {
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_DIAG_ACYA, LOCK);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_TXCC_CTRL, TX_TXCC_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_DRV, TX_DRV);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_MGNFS,
mgnfs_val[link_rate_index][voltage_swing][pre_emphasis]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_CPOST,
cpost_val[link_rate_index][voltage_swing][pre_emphasis]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_CPOST1,
cpost_val[link_rate_index][voltage_swing][pre_emphasis]);
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_DIAG_ACYA, UNLOCK);
} else {
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_DIAG_ACYA, LOCK);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_TXCC_CTRL, TX_TXCC_CTRL);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_DRV, TX_DRV);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_MGNFS,
mgnfs_val[link_rate_index][voltage_swing][pre_emphasis]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_CPOST,
cpost_val[link_rate_index][voltage_swing][pre_emphasis]);
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_DIAG_ACYA, UNLOCK);
}
}
static int px210_dp_hw_init_phy(struct phytium_dp_device *phytium_dp)
{
int port = phytium_dp->port;
int i = 0, data, tmp, mask;
int timeout = 500, ret = 0;
if (port == 0 || port == 1) {
phytium_phy_writel(phytium_dp, PX210_PHY0_APB_RESET, APB_RESET);
phytium_phy_writel(phytium_dp, PX210_PHY0_PIPE_RESET, RESET);
/* config lane to dp mode */
data = 0;
mask = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++) {
data |= (LANE_BIT << i*LANE_BIT_SHIFT);
mask |= (((1<<LANE_BIT_SHIFT) - 1) << (i*LANE_BIT_SHIFT));
}
mask = (mask << (port*LANE_BIT_SHIFT*4));
data = (data << (port*LANE_BIT_SHIFT*4));
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_MODE);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_MODE, tmp);
/* config lane master or slave */
data = 0;
mask = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++) {
data |= (LANE_MASTER << i*LANE_MASTER_SHIFT);
mask |= (((1<<LANE_MASTER_SHIFT) - 1) << (i*LANE_MASTER_SHIFT));
}
mask = (mask << (port*LANE_MASTER_SHIFT*4));
data = (data << (port*LANE_MASTER_SHIFT*4));
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_LINK_CFG);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_LINK_CFG, tmp);
/* pll clock enable */
data = 0;
mask = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++) {
data |= (PLL_EN << i*PLL_EN_SHIFT);
mask |= (((1<<PLL_EN_SHIFT) - 1) << (i*PLL_EN_SHIFT));
}
mask = (mask << (port*PLL_EN_SHIFT*4));
data = (data << (port*PLL_EN_SHIFT*4));
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PLL_EN);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL_EN, tmp);
/* config input 20 bit */
data = 0;
mask = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++) {
data |= (BIT_20 << i*BIT_20_SHIFT);
mask |= (((1<<BIT_20_SHIFT) - 1) << (i*BIT_20_SHIFT));
}
mask = (mask << (port*BIT_20_SHIFT*4));
data = (data << (port*BIT_20_SHIFT*4));
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA_WIDTH);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_PMA_WIDTH, tmp);
/* config lane active power state */
data = 0;
mask = 0;
for (i = 0; i < phytium_dp->source_max_lane_count; i++) {
data |= (A0_ACTIVE << i*A0_ACTIVE_SHIFT);
mask |= (((1<<A0_ACTIVE_SHIFT) - 1) << (i*A0_ACTIVE_SHIFT));
}
if (port == 0) {
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA0_POWER);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_PMA0_POWER, tmp);
} else {
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA1_POWER);
tmp = (tmp & (~mask)) | data;
phytium_phy_writel(phytium_dp, PX210_PHY0_PMA1_POWER, tmp);
}
/* link reset */
mask = (LINK_RESET_MASK << (0*LINTK_RESET_SHIFT)) |
(LINK_RESET_MASK << (1*LINTK_RESET_SHIFT));
data = (LINK_RESET << (0*LINTK_RESET_SHIFT)) |
(LINK_RESET << (1*LINTK_RESET_SHIFT));
tmp = (data & mask);
phytium_phy_writel(phytium_dp, PX210_PHY0_LINK_RESET, tmp);
/* config double link */
if (port == 0)
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL_CFG, SINGLE_LINK);
else if (port == 1)
phytium_phy_writel(phytium_dp, PX210_PHY0_PLL_CFG, DOUBLE_LINK);
/* pipe reset */
phytium_phy_writel(phytium_dp, PX210_PHY0_PIPE_RESET, RESET_DEASSERT);
if (port == 0)
mask = PLL0_LOCK_DONE;
else
mask = PLL1_LOCK_DONE;
do {
mdelay(1);
timeout--;
tmp = phytium_phy_readl(phytium_dp, PX210_PHY0_PMA_CONTROL2);
} while ((!(tmp & mask)) && timeout);
if (timeout == 0) {
DRM_ERROR("reset dp(%d) phy failed\n", port);
ret = -1;
}
udelay(1);
} else {
/* APB reset */
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_APB_RESET);
phytium_phy_writel(phytium_dp, PX210_PHY1_APB_RESET, tmp & (~PHY1_APB_RESET));
phytium_phy_writel(phytium_dp, PX210_PHY1_APB_RESET, tmp | PHY1_APB_RESET);
/* pipe reset */
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_PIPE_RESET);
phytium_phy_writel(phytium_dp, PX210_PHY1_PIPE_RESET,
tmp & (~PHY1_PIPE_RESET_DEASSERT));
/* config MODE/SEL to dp */
mask = PHY1_DP_LANE_BIT << PHY1_DP_LANE_BIT_SHIFT;
data = PHY1_DP_LANE_BIT << PHY1_DP_LANE_BIT_SHIFT;
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_SEL);
tmp = (tmp & (~mask));
phytium_phy_writel(phytium_dp, PX210_PHY1_SEL, tmp);
/* config pll clock enable, input 20 bit, lane active power state */
tmp = (PHY1_PLL_EN << PHY1_PLL_EN_SHIFT) | (PHY1_BIT_20 << PHY1_BIT_20_SHIFT)
| (PHY1_A0_ACTIVE << PHY1_A0_ACTIVE_SHIFT);
phytium_phy_writel(phytium_dp, PX210_PHY1_PMA_MISC, tmp);
/* config single link */
phytium_phy_writel(phytium_dp, PX210_PHY1_PLL_CFG, SINGLE_LINK);
/* pipe reset */
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_PIPE_RESET);
phytium_phy_writel(phytium_dp, PX210_PHY1_PIPE_RESET,
tmp | PHY1_PIPE_RESET_DEASSERT);
mask = PLL0_LOCK_DONE;
do {
mdelay(1);
timeout--;
tmp = phytium_phy_readl(phytium_dp, PX210_PHY1_PMA_CONTROL2);
} while ((!(tmp & mask)) && timeout);
if (timeout == 0) {
DRM_ERROR("reset dp(%d) phy failed\n", port);
ret = -1;
}
udelay(1);
}
mdelay(10);
return ret;
}
static void px210_dp_hw_poweron_panel(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
uint32_t group_offset = priv->dcreq_reg_base[port];
int ret = 0;
phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | PANEL_POWER_ENABLE,
group_offset, PX210_DCREQ_CMD_REGISTER);
ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER,
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to poweron panel\n", __func__);
}
static void px210_dp_hw_poweroff_panel(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
uint32_t group_offset = priv->dcreq_reg_base[port];
int ret = 0;
phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | PANEL_POWER_DISABLE,
group_offset, PX210_DCREQ_CMD_REGISTER);
ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER,
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to poweroff panel\n", __func__);
}
static void px210_dp_hw_enable_backlight(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port, ret = 0;
uint32_t group_offset = priv->dcreq_reg_base[port];
phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | BACKLIGHT_ENABLE,
group_offset, PX210_DCREQ_CMD_REGISTER);
ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER,
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to enable backlight\n", __func__);
}
static void px210_dp_hw_disable_backlight(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
uint32_t group_offset = priv->dcreq_reg_base[port];
int ret = 0;
phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | BACKLIGHT_DISABLE,
group_offset, PX210_DCREQ_CMD_REGISTER);
ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER,
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to disable backlight\n", __func__);
}
static uint32_t px210_dp_hw_get_backlight(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int config;
uint32_t group_offset = priv->address_transform_base;
config = phytium_readl_reg(priv, group_offset, PX210_DC_ADDRESS_TRANSFORM_BACKLIGHT_VALUE);
return ((config >> BACKLIGHT_VALUE_SHIFT) & BACKLIGHT_VALUE_MASK);
}
static int px210_dp_hw_set_backlight(struct phytium_dp_device *phytium_dp, uint32_t level)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
uint32_t group_offset = priv->dcreq_reg_base[port];
int config = 0;
int ret = 0;
if (level > PX210_DP_BACKLIGHT_MAX) {
ret = -EINVAL;
goto out;
}
config = FLAG_REQUEST | CMD_BACKLIGHT | ((level & BACKLIGHT_MASK) << BACKLIGHT_SHIFT);
phytium_writel_reg(priv, config, group_offset, PX210_DCREQ_CMD_REGISTER);
ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER,
FLAG_REQUEST, FLAG_REPLY);
if (ret < 0)
DRM_ERROR("%s: failed to set backlight\n", __func__);
out:
return ret;
}
bool px210_dp_hw_spread_is_enable(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port, config;
uint32_t group_offset = priv->address_transform_base;
config = phytium_readl_reg(priv, group_offset, PX210_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS);
return ((config & DP_SPREAD_ENABLE(port)) ? true:false);
}
int px210_dp_hw_reset(struct phytium_dp_device *phytium_dp)
{
struct drm_device *dev = phytium_dp->dev;
struct phytium_display_private *priv = dev->dev_private;
int port = phytium_dp->port;
int timeout = 100, config, ret = 0;
uint32_t group_offset = priv->address_transform_base;
uint32_t group_offset_dp = priv->dp_reg_base[port];
config = phytium_readl_reg(priv, group_offset, PX210_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS);
config &= (~DC_DP_RESET_STATUS(port));
phytium_writel_reg(priv, config, group_offset, PX210_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS);
phytium_writel_reg(priv, FLAG_REQUEST | CMD_DC_DP_RESET,
priv->dcreq_reg_base[port], PX210_DCREQ_CMD_REGISTER);
do {
mdelay(10);
timeout--;
config = phytium_readl_reg(priv, group_offset,
PX210_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS);
if (config & DC_DP_RESET_STATUS(port))
break;
} while (timeout);
if (timeout == 0) {
DRM_ERROR("reset dc/dp pipe(%d) failed\n", port);
ret = -1;
}
phytium_writel_reg(priv, AUX_CLK_DIVIDER, group_offset_dp, PHYTIUM_DP_AUX_CLK_DIVIDER);
return ret;
}
uint8_t px210_dp_hw_get_source_lane_count(struct phytium_dp_device *phytium_dp)
{
return px210_dp_source_lane_count[phytium_dp->port];
}
static struct phytium_dp_func px210_dp_funcs = {
.dp_hw_get_source_lane_count = px210_dp_hw_get_source_lane_count,
.dp_hw_reset = px210_dp_hw_reset,
.dp_hw_spread_is_enable = px210_dp_hw_spread_is_enable,
.dp_hw_set_backlight = px210_dp_hw_set_backlight,
.dp_hw_get_backlight = px210_dp_hw_get_backlight,
.dp_hw_disable_backlight = px210_dp_hw_disable_backlight,
.dp_hw_enable_backlight = px210_dp_hw_enable_backlight,
.dp_hw_poweroff_panel = px210_dp_hw_poweroff_panel,
.dp_hw_poweron_panel = px210_dp_hw_poweron_panel,
.dp_hw_init_phy = px210_dp_hw_init_phy,
.dp_hw_set_phy_lane_setting = px210_dp_hw_set_phy_lane_setting,
.dp_hw_set_phy_lane_and_rate = px210_dp_hw_set_phy_lane_and_rate,
};
void px210_dp_func_register(struct phytium_dp_device *phytium_dp)
{
phytium_dp->funcs = &px210_dp_funcs;
}

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PX210_DP_H__
#define __PX210_DP_H__
#define PX210_DP_BACKLIGHT_MAX 100
void px210_dp_func_register(struct phytium_dp_device *phytium_dp);
#endif /* __PX210_DP_H__ */

View File

@ -0,0 +1,349 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Phytium display drm driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#ifndef __PX210_REG_H__
#define __PX210_REG_H__
#include "phytium_reg.h"
/******************************dc register start******************************************/
#define PX210_DC_CLOCK_CONTROL 0x0000
#define SOFT_RESET (1<<12)
#define PX210_DC_CLOCK_IDLE 0x0004
#define IS_IDLE (1<<16)
/******************************dc register end********************************************/
/******************************dcreq register start**************************************/
#define PX210_DCREQ_PLANE0_ADDR_START 0x00
#define PX210_DCREQ_PLANE0_ADDR_END 0x04
#define PX210_DCREQ_PLANE1_ADDR_START 0x08
#define PX210_DCREQ_PLANE1_ADDR_END 0x0c
#define PX210_DCREQ_PLANE0_CONFIG 0x10
#define DCREQ_NO_LOSSY (0 << 0)
#define DCREQ_LOSSY (1 << 0)
#define DCREQ_TILE_TYPE_MASK (0x3 << 1)
#define DCREQ_TILE_TYPE_MODE0 (0x1 << 1)
#define DCREQ_TILE_TYPE_MODE3 (0x2 << 1)
#define DCREQ_COLOURFORMAT_MASK (0x7f << 8)
#define DCREQ_COLOURFORMAT_RGB565 (0x5 << 8)
#define DCREQ_COLOURFORMAT_ARGB1555 (0x4 << 8)
#define DCREQ_COLOURFORMAT_ARGB4444 (0x02 << 8)
#define DCREQ_COLOURFORMAT_BGRA8888 (0x29 << 8)
#define DCREQ_COLOURFORMAT_ARGB2101010 (0xe << 8)
#define DCREQ_COLOURFORMAT_YUYV (0x59 << 8)
#define DCREQ_COLOURFORMAT_UYVY (0x5b << 8)
#define DCREQ_ARGBSWIZZLE_MASK (0xf << 4)
#define DCREQ_ARGBSWIZZLE_ARGB (0X0 << 4)
#define DCREQ_ARGBSWIZZLE_BGRA (0XC << 4)
#define DCREQ_MODE_MASK (1 << 16)
#define DCREQ_MODE_LINEAR (0 << 16)
#define DCREQ_MODE_TILE (1 << 16)
#define PX210_DCREQ_PLANE1_CONFIG(pipe) 0x14
#define PX210_DCREQ_PLANE0_CLEAR_COLOR_L 0x18
#define PX210_DCREQ_PLANE0_CLEAR_COLOR_H 0x1C
#define PX210_DCREQ_PLANE1_CLEAR_COLOR_L 0x20
#define PX210_DCREQ_PLANE1_CLEAR_COLOR_H 0x24
#define PX210_DCREQ_CMD_REGISTER 0x38
#define FLAG_REPLY (1<<31)
#define FLAG_REQUEST (1<<30)
#define CMD_PIXEL_CLOCK (0x0 << 28)
#define CMD_BACKLIGHT (0x1 << 28)
#define CMD_DC_DP_RESET (0x3 << 28)
#define BACKLIGHT_SHIFT 21
#define BACKLIGHT_MASK 0x7f
#define BACKLIGHT_MAX 100
#define BACKLIGHT_ENABLE (101 << BACKLIGHT_SHIFT)
#define BACKLIGHT_DISABLE (102 << BACKLIGHT_SHIFT)
#define PANEL_POWER_ENABLE (103 << BACKLIGHT_SHIFT)
#define PANEL_POWER_DISABLE (104 << BACKLIGHT_SHIFT)
#define PIXEL_CLOCK_MASK (0x1fffff)
#define PX210_DCREQ_FBCD_CLOCK_CONFIG 0x3c
#define PX210_DCREQ_PIX_DMA_PREFIX 0x50
#define PREFIX_MASK 0xff
#define PREFIX_SHIFT 32
#define PX210_DCREQ_FRAME_START 0x54
#define PX210_DCREQ_FILTER_CONFIG 0x58
#define PX210_DCREQ_CONTROL 0x5C
#define DC_REQ_ENABLE (1<<0)
#define PX210_DCREQ_MSI_CLEAR 0x60
#define MSI_CLEAR 0x0
#define PX210_DCREQ_RESET 0x68
#define DCREQ_RESET (0x3 << 0)
#define DCREQ_RESET_MASK 0x3
#define PX210_DCREQ_PLAN 0x94
#define DCREQ_PLAN_A 0x0
#define DCREQ_PLAN_B 0X5
/******************************dcreq register end**************************************/
/******************************address transform register start**************************/
#define PX210_GPU_ADDRESS_TRANSFORM_SRC_ADDR 0x0
#define PX210_GPU_ADDRESS_TRANSFORM_SIZE 0x4
#define PX210_GPU_ADDRESS_TRANSFORM_DST_ADDR 0x8
#define PX210_DC_ADDRESS_TRANSFORM_SRC_ADDR 0x24
#define SRC_ADDR_OFFSET 22
#define SRC_ADDR_MASK 0xffffffffff
#define PX210_DC_ADDRESS_TRANSFORM_SIZE 0x28
#define ADDRESS_TRANSFORM_ENABLE (0x1 << 31)
#define SIZE_OFFSET 22
#define PX210_DC_ADDRESS_TRANSFORM_DST_ADDR 0x2c
#define DST_ADDR_OFFSET 22
#define PX210_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS 0x48
#define DC_DP_RESET_STATUS(pipe) (1 << pipe)
#define DP_SPREAD_ENABLE(pipe) (0x8 << pipe)
#define PX210_DC_ADDRESS_TRANSFORM_BACKLIGHT_VALUE 0x4c
#define BACKLIGHT_VALUE_MASK (0x7f)
#define BACKLIGHT_VALUE_SHIFT 16
/******************************address transform register end**************************/
/******************************phy register start******************************************/
/* self define */
#define PX210_PHY0_PIPE_RESET 0x40104
#define RESET 0x0
#define RESET_DEASSERT 0x1
#define PX210_PHY1_PIPE_RESET 0x100100
#define PHY1_PIPE_RESET 0x0
#define PHY1_PIPE_RESET_DEASSERT 0x4
#define PX210_PHY1_EN_REFCLK 0x100070
#define PX210_PHY0_MODE 0x40088
#define LANE_BIT (0x3)
#define LANE_BIT_SHIFT 0x2
#define PX210_PHY1_SEL 0x100004
#define PHY1_DP_LANE_BIT 0x1
#define PHY1_DP_LANE_BIT_SHIFT 2
#define PX210_PHY0_LINK_CFG 0x40044
#define LANE_MASTER 0x1
#define LANE_MASTER_SHIFT 1
#define PX210_PHY0_PLL_EN 0x40010
#define PLL_EN 0x1
#define PLL_EN_SHIFT 1
#define PX210_PHY0_PMA_WIDTH 0x40020
#define BIT_20 0x5
#define BIT_20_SHIFT 4
#define PX210_PHY0_PMA0_POWER 0x40014
#define PX210_PHY0_PMA1_POWER 0x40018
#define A0_ACTIVE 0x1
#define A0_ACTIVE_SHIFT 8
#define A3_POWERDOWN3 0x8
#define A3_POWERDOWN3_SHIFT 8
#define PX210_PHY1_PMA_MISC 0x1000a0
#define PHY1_PLL_EN 0x1
#define PHY1_PLL_EN_MASK 1
#define PHY1_PLL_EN_SHIFT 8
#define PHY1_BIT_20 0x5
#define PHY1_BIT_20_SHIFT 9
#define PHY1_A0_ACTIVE 0x1
#define PHY1_A0_ACTIVE_SHIFT 2
#define PHY1_A0_ACTIVE_MASK 0x3f
#define PHY1_A3_POWERDOWN3 0x8
#define PHY1_A3_POWERDOWN3_MASK 0x3f
#define PHY1_A3_POWERDOWN3_SHIFT 2
#define PX210_PHY0_LINK_RESET 0x40108
#define LINK_RESET 0x1
#define LINK_RESET_MASK 0x1
#define LINTK_RESET_SHIFT 0x1
#define PX210_PHY0_APB_RESET 0x40100
#define APB_RESET 0x1
#define PX210_PHY1_APB_RESET 0x100104
#define PHY1_APB_RESET 0x4
/* phy origin register */
#define PX210_PHY0_PLL_CFG 0x30038
#define PX210_PHY1_PLL_CFG 0xb0038
#define SINGLE_LINK 0x0
#define DOUBLE_LINK 0x2
#define PX210_PHY0_PMA_CONTROL 0x3800c
#define PX210_PHY1_PMA_CONTROL 0xb800c
#define CONTROL_ENABLE 0x1
#define CONTROL_ENABLE_MASK 0x1
#define CONTROL_ENABLE_SHIFT 0x1
#define PX210_PHY0_PMA_CONTROL2 0x38004
#define PX210_PHY1_PMA_CONTROL2 0xb8004
#define PLL0_LOCK_DONE (0x1 << 6)
#define PLL1_LOCK_DONE (0x1 << 7)
#define PX210_PHY0_PLL0_CLK_SEL 0X684
#define PX210_PHY0_PLL1_CLK_SEL 0x704
#define PX210_PHY1_PLL_CLK_SEL 0X80684
#define PLL_LINK_RATE_162000 0xf01
#define PLL_LINK_RATE_270000 0x701
#define PLL_LINK_RATE_540000 0x301
#define PLL_LINK_RATE_810000 0x200
#define PX210_PHY0_HSCLK0_SEL 0x18398
#define PX210_PHY0_HSCLK1_SEL 0x1a398
#define PX210_PHY1_HSCLK_SEL 0x90398
#define HSCLK_LINK_0 0x0
#define HSCLK_LINK_1 0x1
#define PX210_PHY0_HSCLK0_DIV 0x1839c
#define PX210_PHY0_HSCLK1_DIV 0x1a39c
#define PX210_PHY1_HSCLK_DIV 0x9039c
#define HSCLK_LINK_RATE_162000 0x2
#define HSCLK_LINK_RATE_270000 0x1
#define HSCLK_LINK_RATE_540000 0x0
#define HSCLK_LINK_RATE_810000 0x0
#define PX210_PHY0_PLLDRC0_CTRL 0x18394
#define PX210_PHY0_PLLDRC1_CTRL 0x1a394
#define PX210_PHY1_PLLDRC_CTRL 0x90394
#define PLLDRC_LINK0 0x1
#define PLLDRC_LINK1 0x9
#define PX210_PHY0_PLL0_DSM_M0 0x250
#define PX210_PHY1_PLL0_DSM_M0 0x80250
#define PLL0_DSM_M0 0x4
#define PX210_PHY0_PLL0_VCOCAL_START 0x218
#define PX210_PHY1_PLL0_VCOCAL_START 0x80218
#define PLL0_VCOCAL_START 0xc5e
#define PX210_PHY0_PLL0_VCOCAL_CTRL 0x208
#define PX210_PHY1_PLL0_VCOCAL_CTRL 0x80208
#define PLL0_VCOCAL_CTRL 0x3
#define PX210_PHY0_PLL1_DSM_M0 0x350
#define PLL1_DSM_M0 0x4
#define PX210_PHY0_PLL1_VCOCAL_START 0x318
#define PLL1_VCOCAL_START 0xc5e
#define PX210_PHY0_PLL1_VCOCAL_CTRL 0x308
#define PLL1_VCOCAL_CTRL 0x3
#define PX210_PHY0_PLL0_CP_PADJ 0x690
#define PX210_PHY0_PLL0_CP_IADJ 0x694
#define PX210_PHY0_PLL0_CP_FILT_PADJ 0x698
#define PX210_PHY0_PLL0_INTDIV 0x240
#define PX210_PHY0_PLL0_FRACDIVL 0x244
#define PX210_PHY0_PLL0_FRACDIVH 0x248
#define PX210_PHY0_PLL0_HIGH_THR 0x24c
#define PX210_PHY0_PLL0_PDIAG_CTRL 0x680
#define PX210_PHY0_PLL0_VCOCAL_PLLCNT_START 0x220
#define PX210_PHY0_PLL0_LOCK_PEFCNT 0x270
#define PX210_PHY0_PLL0_LOCK_PLLCNT_START 0x278
#define PX210_PHY0_PLL0_LOCK_PLLCNT_THR 0x27c
#define PX210_PHY0_PLL1_CP_PADJ 0x710
#define PX210_PHY0_PLL1_CP_IADJ 0x714
#define PX210_PHY0_PLL1_CP_FILT_PADJ 0x718
#define PX210_PHY0_PLL1_INTDIV 0x340
#define PX210_PHY0_PLL1_FRACDIVL 0x344
#define PX210_PHY0_PLL1_FRACDIVH 0x348
#define PX210_PHY0_PLL1_HIGH_THR 0x34c
#define PX210_PHY0_PLL1_PDIAG_CTRL 0x700
#define PX210_PHY0_PLL1_VCOCAL_PLLCNT_START 0x320
#define PX210_PHY0_PLL1_LOCK_PEFCNT 0x370
#define PX210_PHY0_PLL1_LOCK_PLLCNT_START 0x378
#define PX210_PHY0_PLL1_LOCK_PLLCNT_THR 0x37c
#define PX210_PHY1_PLL0_CP_PADJ 0x80690
#define PX210_PHY1_PLL0_CP_IADJ 0x80694
#define PX210_PHY1_PLL0_CP_FILT_PADJ 0x80698
#define PX210_PHY1_PLL0_INTDIV 0x80240
#define PX210_PHY1_PLL0_FRACDIVL 0x80244
#define PX210_PHY1_PLL0_FRACDIVH 0x80248
#define PX210_PHY1_PLL0_HIGH_THR 0x8024c
#define PX210_PHY1_PLL0_PDIAG_CTRL 0x80680
#define PX210_PHY1_PLL0_VCOCAL_PLLCNT_START 0x80220
#define PX210_PHY1_PLL0_LOCK_PEFCNT 0x80270
#define PX210_PHY1_PLL0_LOCK_PLLCNT_START 0x80278
#define PX210_PHY1_PLL0_LOCK_PLLCNT_THR 0x8027c
#define PX210_PHY0_PLL0_TX_PSC_A0 0x18400
#define PX210_PHY1_PLL0_TX_PSC_A0 0x90400
#define PLL0_TX_PSC_A0 0xfb
#define PX210_PHY0_PLL0_TX_PSC_A2 0x18408
#define PX210_PHY1_PLL0_TX_PSC_A2 0x90408
#define PLL0_TX_PSC_A2 0x4aa
#define PX210_PHY0_PLL0_TX_PSC_A3 0x1840c
#define PX210_PHY1_PLL0_TX_PSC_A3 0x9040c
#define PLL0_TX_PSC_A3 0x4aa
#define PX210_PHY0_PLL0_RX_PSC_A0 0x28000
#define PX210_PHY1_PLL0_RX_PSC_A0 0xa0000
#define PLL0_RX_PSC_A0 0x0
#define PX210_PHY0_PLL0_RX_PSC_A2 0x28008
#define PX210_PHY1_PLL0_RX_PSC_A2 0xa0008
#define PLL0_RX_PSC_A2 0x0
#define PX210_PHY0_PLL0_RX_PSC_A3 0x2800C
#define PX210_PHY1_PLL0_RX_PSC_A3 0xa000C
#define PLL0_RX_PSC_A3 0x0
#define PX210_PHY0_PLL0_RX_PSC_CAL 0x28018
#define PX210_PHY1_PLL0_RX_PSC_CAL 0xa0018
#define PLL0_RX_PSC_CAL 0x0
#define PX210_PHY0_PLL1_TX_PSC_A0 0x1a400
#define PLL1_TX_PSC_A0 0xfb
#define PX210_PHY0_PLL1_TX_PSC_A2 0x1a408
#define PLL1_TX_PSC_A2 0x4aa
#define PX210_PHY0_PLL1_TX_PSC_A3 0x1a40c
#define PLL1_TX_PSC_A3 0x4aa
#define PX210_PHY0_PLL1_RX_PSC_A0 0x2a000
#define PLL1_RX_PSC_A0 0x0
#define PX210_PHY0_PLL1_RX_PSC_A2 0x2a008
#define PLL1_RX_PSC_A2 0x0
#define PX210_PHY0_PLL1_RX_PSC_A3 0x2a00C
#define PLL1_RX_PSC_A3 0x0
#define PX210_PHY0_PLL1_RX_PSC_CAL 0x2a018
#define PLL1_RX_PSC_CAL 0x0
#define PX210_PHY0_PLL0_XCVR_CTRL 0x183a8
#define PX210_PHY1_PLL0_XCVR_CTRL 0x903a8
#define PLL0_XCVR_CTRL 0xf
#define PX210_PHY0_PLL1_XCVR_CTRL 0x1a3a8
#define PLL1_XCVR_CTRL 0xf
#define PX210_PHY0_PLL0_RX_GCSM1_CTRL 0x28420
#define PX210_PHY1_PLL0_RX_GCSM1_CTRL 0xa0420
#define PLL0_RX_GCSM1_CTRL 0x0
#define PX210_PHY0_PLL0_RX_GCSM2_CTRL 0x28440
#define PX210_PHY1_PLL0_RX_GCSM2_CTRL 0xa0440
#define PLL0_RX_GCSM2_CTRL 0x0
#define PX210_PHY0_PLL0_RX_PERGCSM_CTRL 0x28460
#define PX210_PHY1_PLL0_RX_PERGCSM_CTRL 0xa0460
#define PLL0_RX_PERGCSM_CTRL 0x0
#define PX210_PHY0_PLL1_RX_GCSM1_CTRL 0x2a420
#define PLL1_RX_GCSM1_CTRL 0x0
#define PX210_PHY0_PLL1_RX_GCSM2_CTRL 0x2a440
#define PLL1_RX_GCSM2_CTRL 0x0
#define PX210_PHY0_PLL1_RX_PERGCSM_CTRL 0x2a460
#define PLL1_RX_PERGCSM_CTRL 0x0
/* swing and emphasis */
#define PX210_PHY0_PLL0_TX_DIAG_ACYA 0x1879c
#define PX210_PHY0_PLL1_TX_DIAG_ACYA 0x1a79c
#define PX210_PHY1_PLL0_TX_DIAG_ACYA 0x9079c
#define LOCK 1
#define UNLOCK 0
#define PX210_PHY0_PLL0_TX_TXCC_CTRL 0x18100
#define PX210_PHY0_PLL1_TX_TXCC_CTRL 0x1a100
#define PX210_PHY1_PLL0_TX_TXCC_CTRL 0x90100
#define TX_TXCC_CTRL 0x8a4
#define PX210_PHY0_PLL0_TX_DRV 0x18318
#define PX210_PHY0_PLL1_TX_DRV 0x1a318
#define PX210_PHY1_PLL0_TX_DRV 0x90318
#define TX_DRV 0x3
#define PX210_PHY0_PLL0_TX_MGNFS 0x18140
#define PX210_PHY0_PLL1_TX_MGNFS 0x1a140
#define PX210_PHY1_PLL0_TX_MGNFS 0x90140
#define PX210_PHY0_PLL0_TX_CPOST 0x18130
#define PX210_PHY0_PLL1_TX_CPOST 0x1a130
#define PX210_PHY0_PLL1_TX_CPOST1 0x1a13c
#define PX210_PHY1_PLL0_TX_CPOST 0x90130
/******************************phy register end********************************************/
#endif /* __PX210_REG_H__ */