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:
parent
823ace9213
commit
9debb2ab33
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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");
|
|
@ -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
|
@ -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__ */
|
|
@ -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);
|
||||
}
|
|
@ -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__ */
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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 = ∁
|
||||
|
||||
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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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,
|
||||
®_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;
|
||||
}
|
||||
}
|
|
@ -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__ */
|
|
@ -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,
|
||||
};
|
|
@ -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__ */
|
|
@ -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);
|
||||
}
|
|
@ -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__ */
|
|
@ -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,
|
||||
};
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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);
|
||||
}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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__ */
|
Loading…
Reference in New Issue