From: Michal Wilczynski Date: Wed, 28 Aug 2024 07:49:03 +0000 (+0200) Subject: drm: spacemit: Add spacemit DRM drivers X-Git-Tag: accepted/tizen/unified/x/20240911.015644~14 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=359ef2dc6d7b039a9de5b3652bf737638bf86092;p=platform%2Fkernel%2Flinux-riscv.git drm: spacemit: Add spacemit DRM drivers Copy the drivers as is from the vendor kernel [1], since the vendor already ported them to the kernel 6.6. [1] - https://github.com/BPI-SINOVOIP/pi-linux/tree/linux-6.6.36-k1 Change-Id: I1329be3f40f6fabc5240cb42c61ee5a060d8d3da Signed-off-by: Michal Wilczynski --- diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 120c9f3473be..958f9544e3a9 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -390,6 +390,8 @@ source "drivers/gpu/drm/sprd/Kconfig" source "drivers/gpu/drm/img-rogue/Kconfig" +source "drivers/gpu/drm/spacemit/Kconfig" + config DRM_HYPERV tristate "DRM Support for Hyper-V synthetic video device" depends on DRM && PCI && MMU && HYPERV diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index a088f8be0915..f98bc4bb20c5 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -199,3 +199,4 @@ obj-y += solomon/ obj-$(CONFIG_DRM_SPRD) += sprd/ obj-$(CONFIG_DRM_LOONGSON) += loongson/ obj-$(CONFIG_POWERVR_ROGUE) += img-rogue/ +obj-$(CONFIG_DRM_SPACEMIT) += spacemit/ diff --git a/drivers/gpu/drm/spacemit/Kconfig b/drivers/gpu/drm/spacemit/Kconfig new file mode 100644 index 000000000000..67de8a3b9f72 --- /dev/null +++ b/drivers/gpu/drm/spacemit/Kconfig @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0 + +config DRM_SPACEMIT + tristate "DRM Support for Spacemit" + select DRM + select DRM_KMS_HELPER + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + select DRM_MIPI_DSI + select DRM_PANEL + select VIDEOMODE_HELPERS + select BACKLIGHT_CLASS_DEVICE + select GKI_FIX_WORKAROUND if DRM_SPACEMIT=m + default n + help + Choose this option if you have a Spacemit soc chipsets. + This driver provides Spacemit kernel mode + setting and buffer management. If M is selected the module will be called spacemit-drm. + +config SPACEMIT_MIPI_PANEL + tristate "MIPI Panel Support For Spacemit" + depends on DRM_SPACEMIT + +config SPACEMIT_HDMI + tristate "HDMI Support For Spacemit" + depends on DRM_SPACEMIT + +config DRM_LT8911EXB + tristate "Lontium LT8911EXB DSI to eDP" + default y + depends on DRM_SPACEMIT + select DRM_KMS_HELPER + select REGMAP_I2C + select DRM_PANEL + select DRM_MIPI_DSI + select AUXILIARY_BUS + select DRM_DP_AUX_BUS + + help + Support for Lontium LT8911EXB DSI to eDP driver. + +config DRM_LT9711 + tristate "Lontium LT9711 DSI to DP" + default y + depends on DRM_SPACEMIT + select DRM_KMS_HELPER + select REGMAP_I2C + select DRM_PANEL + select DRM_MIPI_DSI + select AUXILIARY_BUS + select DRM_DP_AUX_BUS + + help + Support for Lontium LT9711 DSI to eDP driver. \ No newline at end of file diff --git a/drivers/gpu/drm/spacemit/Makefile b/drivers/gpu/drm/spacemit/Makefile new file mode 100644 index 000000000000..00582f9e6385 --- /dev/null +++ b/drivers/gpu/drm/spacemit/Makefile @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0 + +spacemit-drm-y := spacemit_drm.o \ + spacemit_cmdlist.o \ + spacemit_dpu.o \ + spacemit_planes.o \ + spacemit_dsi.o \ + spacemit_wb.o \ + spacemit_dphy.o \ + spacemit_lib.o \ + spacemit_gem.o \ + spacemit_dmmu.o \ + spacemit_bootloader.o \ + sysfs/sysfs_class.o \ + sysfs/sysfs_dpu.o \ + sysfs/sysfs_dsi.o \ + sysfs/sysfs_dphy.o \ + sysfs/sysfs_mipi_panel.o \ + dpu/dpu_debug.o \ + dpu/dpu_saturn.o \ + dpu/saturn_fbcmem.o \ + dsi/spacemit_dsi_drv.o \ + dsi/spacemit_dptc_drv.o \ + dphy/spacemit_dphy_drv.o + + +obj-$(CONFIG_DRM_SPACEMIT) += spacemit-drm.o + +obj-$(CONFIG_SPACEMIT_MIPI_PANEL) += spacemit_mipi_panel.o +obj-$(CONFIG_SPACEMIT_HDMI) += spacemit_hdmi.o +obj-$(CONFIG_DRM_LT8911EXB) += lt8911exb.o +obj-$(CONFIG_DRM_LT9711) += lt9711.o diff --git a/drivers/gpu/drm/spacemit/dphy/spacemit_dphy_drv.c b/drivers/gpu/drm/spacemit/dphy/spacemit_dphy_drv.c new file mode 100644 index 000000000000..079332eba7e9 --- /dev/null +++ b/drivers/gpu/drm/spacemit/dphy/spacemit_dphy_drv.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Spacemit Co., Ltd. + * + */ + +#include +#include +#include + +#include "../dsi/spacemit_dsi_hw.h" +#include "../spacemit_dphy.h" +#include "../dsi/spacemit_dptc_drv.h" + +static unsigned int spacemit_dphy_lane[5] = {0, 0x1, 0x3, 0x7, 0xf}; + +static void dphy_ana_reset(void __iomem *base_addr) +{ + dsi_clear_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_RESET); + udelay(5); + dsi_set_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_RESET); +} + +static void dphy_set_power(void __iomem *base_addr, bool poweron) +{ + if(poweron) { + dsi_set_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_RESET); + dsi_set_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_PU); + } else { + dsi_clear_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_PU); + dsi_clear_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_RESET); + } +} + +static void dphy_set_cont_clk(void __iomem *base_addr, bool cont_clk) +{ +#ifdef DPTC_DPHY_TEST + uint32_t tmp; + + if(cont_clk) { + tmp = dptc_dsi_read(0x04); + tmp |= CFG_DPHY_CONT_CLK; + //dptc_dsi_write(0x04, tmp); + } else { + tmp = dptc_dsi_read(0x04); + tmp &= (~CFG_DPHY_CONT_CLK); + //dptc_dsi_write(0x04, tmp); + } + dptc_dsi_write(0x04, 0x30001); +#else + if(cont_clk) + dsi_set_bits(base_addr, DSI_PHY_CTRL_1, CFG_DPHY_CONT_CLK); + else + dsi_clear_bits(base_addr, DSI_PHY_CTRL_1, CFG_DPHY_CONT_CLK); + + dsi_set_bits(base_addr, DSI_PHY_CTRL_1, CFG_DPHY_ADD_VALID); + dsi_set_bits(base_addr, DSI_PHY_CTRL_1, CFG_DPHY_VDD_VALID); +#endif +} + +static void dphy_set_lane_num(void __iomem *base_addr, uint32_t lane_num) +{ +#ifdef DPTC_DPHY_TEST + uint32_t tmp; + + tmp = dptc_dsi_read(0x08); + tmp &= ~CFG_DPHY_LANE_EN_MASK; + tmp |= spacemit_dphy_lane[lane_num] << CFG_DPHY_LANE_EN_SHIFT; + dptc_dsi_write(0x08, 0); + tmp = dptc_dsi_read(0x08); + dptc_dsi_write(0x08, 0x30); +#endif + dsi_write_bits(base_addr, DSI_PHY_CTRL_2, + CFG_DPHY_LANE_EN_MASK, spacemit_dphy_lane[lane_num] << CFG_DPHY_LANE_EN_SHIFT); +} + +static void dphy_set_bit_clk_src(void __iomem *base_addr, uint32_t bit_clk_src, + uint32_t half_pll5) +{ +#ifdef DPTC_DPHY_TEST + uint32_t tmp; +#endif + + if(bit_clk_src >= DPHY_BIT_CLK_SRC_MAX) { + pr_err("%s: Invalid bit clk src (%d)\n", __func__, bit_clk_src); + return; + } + +#ifdef DPTC_DPHY_TEST + //if(bit_clk_src == DPHY_BIT_CLK_SRC_MUX) { + if(0) { + tmp = dptc_dsi_read(0x68); + tmp |= CFG_CLK_SEL; + dptc_dsi_write(0x68,tmp); + } else { + tmp = dptc_dsi_read(0x68); + tmp &= ~CFG_CLK_SEL; + dptc_dsi_write(0x68,tmp); + } + + //if(1 == half_pll5) { + if(0) { + tmp = dptc_dsi_read(0x68); + tmp |= CFG_CLK_DIV2; + dptc_dsi_write(0x68,tmp); + } else { + tmp = dptc_dsi_read(0x68); + tmp &= ~CFG_CLK_DIV2; + dptc_dsi_write(0x68,tmp); + } +#else +#if 0 + if(bit_clk_src == DPHY_BIT_CLK_SRC_MUX) + dsi_set_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_SEL); + else + dsi_clear_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_SEL); + + if(1 == half_pll5) + dsi_set_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_DIV2); + else + dsi_clear_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_DIV2); +#else + /* + dsi_set_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_SEL); + dsi_clear_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_DIV2); + */ +#endif +#endif +} + +static void dphy_set_timing(struct spacemit_dphy_ctx *dphy_ctx) +{ + uint32_t bitclk, lpx_clk, lpx_time, ta_get, ta_go; + int ui, wakeup, reg; + int hs_prep, hs_zero, hs_trail, hs_exit, ck_zero, ck_trail, ck_exit; + int esc_clk, esc_clk_t; + struct spacemit_dphy_timing *phy_timing; + uint32_t value; + + if(NULL == dphy_ctx) { + pr_err("%s: Invalid param!\n", __func__); + return; + } + + phy_timing = &(dphy_ctx->dphy_timing); + + DRM_DEBUG("%s() phy_freq %d esc_clk %d \n", __func__, dphy_ctx->phy_freq, dphy_ctx->esc_clk); + + esc_clk = dphy_ctx->esc_clk/1000; + esc_clk_t = 1000/esc_clk; + + bitclk = dphy_ctx->phy_freq / 1000; + ui = 1000/bitclk + 1; + + lpx_clk = (phy_timing->lpx_constant + phy_timing->lpx_ui * ui) / esc_clk_t + 1; + lpx_time = lpx_clk * esc_clk_t; + + /* Below is for NT35451 */ + ta_get = lpx_time * 5 / esc_clk_t - 1; + ta_go = lpx_time * 4 / esc_clk_t - 1; + + wakeup = phy_timing->wakeup_constant; + wakeup = wakeup / esc_clk_t + 1; + + hs_prep = phy_timing->hs_prep_constant + phy_timing->hs_prep_ui * ui; + hs_prep = hs_prep / esc_clk_t + 1; + + /* Our hardware added 3-byte clk automatically. + * 3-byte 3 * 8 * ui. + */ + hs_zero = phy_timing->hs_zero_constant + phy_timing->hs_zero_ui * ui - + (hs_prep + 1) * esc_clk_t; + hs_zero = (hs_zero - (3 * ui << 3)) / esc_clk_t + 4; + if (hs_zero < 0) + hs_zero = 0; + + hs_trail = phy_timing->hs_trail_constant + phy_timing->hs_trail_ui * ui; + hs_trail = ((8 * ui) >= hs_trail) ? (8 * ui) : hs_trail; + hs_trail = hs_trail / esc_clk_t + 1; + if (hs_trail > 3) + hs_trail -= 3; + else + hs_trail = 0; + + hs_exit = phy_timing->hs_exit_constant + phy_timing->hs_exit_ui * ui; + hs_exit = hs_exit / esc_clk_t + 1; + + ck_zero = phy_timing->ck_zero_constant + phy_timing->ck_zero_ui * ui - + (hs_prep + 1) * esc_clk_t; + ck_zero = ck_zero / esc_clk_t + 1; + + ck_trail = phy_timing->ck_trail_constant + phy_timing->ck_trail_ui * ui; + ck_trail = ck_trail / esc_clk_t + 1; + + ck_exit = hs_exit; + + reg = (hs_exit << CFG_DPHY_TIME_HS_EXIT_SHIFT) + | (hs_trail << CFG_DPHY_TIME_HS_TRAIL_SHIFT) + | (hs_zero << CFG_DPHY_TIME_HS_ZERO_SHIFT) + | (hs_prep << CFG_DPHY_TIME_HS_PREP_SHIFT); + + DRM_DEBUG("%s dphy time0 hs_exit %d hs_trail %d hs_zero %d hs_prep %d reg 0x%x\n", __func__, hs_exit, hs_trail, hs_zero, hs_prep, reg); +#ifdef DPTC_DPHY_TEST + dptc_dsi_write(0x40 , 0x01000000); +#else + dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_0, reg); + // dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_0, 0x06010603); +#endif + + reg = (ta_get << CFG_DPHY_TIME_TA_GET_SHIFT) + | (ta_go << CFG_DPHY_TIME_TA_GO_SHIFT) + | (wakeup << CFG_DPHY_TIME_WAKEUP_SHIFT); + + DRM_DEBUG("%s dphy time1 ta_get %d ta_go %d wakeup %d reg 0x%x\n", __func__, ta_get, ta_go, wakeup, reg); +#ifdef DPTC_DPHY_TEST + dptc_dsi_write(0x44, 0x0403001F); +#else + dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_1, reg); + // dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_1, 0x130fcd98); +#endif + reg = (ck_exit << CFG_DPHY_TIME_CLK_EXIT_SHIFT) + | (ck_trail << CFG_DPHY_TIME_CLK_TRAIL_SHIFT) + | (ck_zero << CFG_DPHY_TIME_CLK_ZERO_SHIFT) + | (lpx_clk << CFG_DPHY_TIME_CLK_LPX_SHIFT); + + DRM_DEBUG("%s dphy time2 ck_exit %d ck_trail %d ck_zero %d lpx_clk %d reg 0x%x\n", __func__, ck_exit, ck_trail, ck_zero, lpx_clk, reg); +#ifdef DPTC_DPHY_TEST + dptc_dsi_write(0x48, 0x02010500); +#else + dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_2, reg); + // dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_2, 0x06040c04); +#endif + + reg = (lpx_clk << CFG_DPHY_TIME_LPX_SHIFT) + | phy_timing->req_ready << CFG_DPHY_TIME_REQRDY_SHIFT; + + DRM_DEBUG("%s dphy time3 lpx_clk %d req_ready %d reg 0x%x\n", __func__, lpx_clk, phy_timing->req_ready, reg); +#ifdef DPTC_DPHY_TEST + dptc_dsi_write(0x4c, 0x001F); +#else + dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_3, reg); + // dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_3, 0x43c); +#endif + /* calculated timing on brownstone: + * DSI_PHY_TIME_0 0x06080204 + * DSI_PHY_TIME_1 0x6d2bfff0 + * DSI_PHY_TIME_2 0x603130a + * DSI_PHY_TIME_3 0xa3c + */ + + value = dsi_read(dphy_ctx->base_addr, DSI_PHY_TIME_0); + DRM_DEBUG("%s() DSI_PHY_TIME_0 offset 0x%x value 0x%x\n", __func__, DSI_PHY_TIME_0, value); + value = dsi_read(dphy_ctx->base_addr, DSI_PHY_TIME_1); + DRM_DEBUG("%s() DSI_PHY_TIME_1 offset 0x%x value 0x%x\n", __func__, DSI_PHY_TIME_1, value); + value = dsi_read(dphy_ctx->base_addr, DSI_PHY_TIME_2); + DRM_DEBUG("%s() DSI_PHY_TIME_2 offset 0x%x value 0x%x\n", __func__, DSI_PHY_TIME_2, value); + value = dsi_read(dphy_ctx->base_addr, DSI_PHY_TIME_3); + DRM_DEBUG("%s() DSI_PHY_TIME_3 offset 0x%x value 0x%x\n", __func__, DSI_PHY_TIME_3, value); +} + +static void dphy_get_setting(struct spacemit_dphy_ctx *dphy_ctx, struct device_node *np) +{ + struct spacemit_dphy_timing *dphy_timing = &dphy_ctx->dphy_timing; + int ret; + + if(NULL == dphy_timing){ + pr_err("%s: Invalid param\n",__func__); + return; + } + + ret = of_property_read_u32(np, "hs_prep_constant", &dphy_timing->hs_prep_constant); + if(0 != ret) + dphy_timing->hs_prep_constant = HS_PREP_CONSTANT_DEFAULT; + + ret = of_property_read_u32(np, "hs_prep_ui", &dphy_timing->hs_prep_ui); + if(0 != ret) + dphy_timing->hs_prep_ui = HS_PREP_UI_DEFAULT; + + ret = of_property_read_u32(np, "hs_zero_constant", &dphy_timing->hs_zero_constant); + if(0 != ret) + dphy_timing->hs_zero_constant = HS_ZERO_CONSTANT_DEFAULT; + + ret = of_property_read_u32(np, "hs_zero_ui", &dphy_timing->hs_zero_ui); + if(0 != ret) + dphy_timing->hs_zero_ui = HS_ZERO_UI_DEFAULT; + + ret = of_property_read_u32(np, "hs_trail_constant", &dphy_timing->hs_trail_constant); + if(0 != ret) + dphy_timing->hs_trail_constant = HS_TRAIL_CONSTANT_DEFAULT; + + ret = of_property_read_u32(np, "hs_trail_ui", &dphy_timing->hs_trail_ui); + if(0 != ret) + dphy_timing->hs_trail_ui = HS_TRAIL_UI_DEFAULT; + + ret = of_property_read_u32(np, "hs_exit_constant", &dphy_timing->hs_exit_constant); + if(0 != ret) + dphy_timing->hs_exit_constant = HS_EXIT_CONSTANT_DEFAULT; + + ret = of_property_read_u32(np, "hs_exit_ui", &dphy_timing->hs_exit_ui); + if(0 != ret) + dphy_timing->hs_exit_ui = HS_EXIT_UI_DEFAULT; + + ret = of_property_read_u32(np, "ck_zero_constant", &dphy_timing->ck_zero_constant); + if(0 != ret) + dphy_timing->ck_zero_constant = CK_ZERO_CONSTANT_DEFAULT; + + ret = of_property_read_u32(np, "ck_zero_ui", &dphy_timing->ck_zero_ui); + if(0 != ret) + dphy_timing->ck_zero_ui = CK_ZERO_UI_DEFAULT; + + ret = of_property_read_u32(np, "ck_trail_constant", &dphy_timing->ck_trail_constant); + if(0 != ret) + dphy_timing->ck_trail_constant = CK_TRAIL_CONSTANT_DEFAULT; + + ret = of_property_read_u32(np, "ck_zero_ui", &dphy_timing->ck_zero_ui); + if(0 != ret) + dphy_timing->ck_zero_ui = CK_TRAIL_UI_DEFAULT; + + ret = of_property_read_u32(np, "req_ready", &dphy_timing->req_ready); + if(0 != ret) + dphy_timing->req_ready = REQ_READY_DEFAULT; + + ret = of_property_read_u32(np, "wakeup_constant", &dphy_timing->wakeup_constant); + if(0 != ret) + dphy_timing->wakeup_constant = WAKEUP_CONSTANT_DEFAULT; + + ret = of_property_read_u32(np, "wakeup_ui", &dphy_timing->wakeup_ui); + if(0 != ret) + dphy_timing->wakeup_ui = WAKEUP_UI_DEFAULT; + + ret = of_property_read_u32(np, "lpx_constant", &dphy_timing->lpx_constant); + if(0 != ret) + dphy_timing->lpx_constant = LPX_CONSTANT_DEFAULT; + + ret = of_property_read_u32(np, "lpx_ui", &dphy_timing->lpx_ui); + if(0 != ret) + dphy_timing->lpx_ui = LPX_UI_DEFAULT; +} + + +void spacemit_dphy_core_get_status(struct spacemit_dphy_ctx *dphy_ctx) +{ + pr_debug("%s\n", __func__); + + if(NULL == dphy_ctx){ + pr_err("%s: Invalid param\n", __func__); + return; + } + + dphy_ctx->dphy_status0 = dsi_read(dphy_ctx->base_addr, DSI_PHY_STATUS_0); + dphy_ctx->dphy_status1 = dsi_read(dphy_ctx->base_addr, DSI_PHY_STATUS_1); + dphy_ctx->dphy_status2 = dsi_read(dphy_ctx->base_addr, DSI_PHY_STATUS_2); + pr_debug("%s: dphy_status0 = 0x%x\n", __func__, dphy_ctx->dphy_status0); + pr_debug("%s: dphy_status1 = 0x%x\n", __func__, dphy_ctx->dphy_status1); + pr_debug("%s: dphy_status2 = 0x%x\n", __func__, dphy_ctx->dphy_status2); +} + +void spacemit_dphy_core_reset(struct spacemit_dphy_ctx *dphy_ctx) +{ + pr_debug("%s\n", __func__); + + if(NULL == dphy_ctx){ + pr_err("%s: Invalid param\n", __func__); + return; + } + + dphy_ana_reset(dphy_ctx->base_addr); +} + +/** + * spacemit_dphy_core_init - int spacemit dphy + * + * @dphy_ctx: pointer to the spacemit_dphy_ctx + * + * This function will be called by the dsi driver in order to init the dphy + * This function will do phy power on, enable continous clk, set dphy timing + * and set lane number. + * + * This function has no return value. + * + */ +void spacemit_dphy_core_init(struct spacemit_dphy_ctx *dphy_ctx) +{ + pr_debug("%s\n", __func__); + + if(NULL == dphy_ctx){ + pr_err("%s: Invalid param\n", __func__); + return; + } + + if(DPHY_STATUS_UNINIT != dphy_ctx->status){ + pr_warn("%s: dphy_ctx has been initialized (%d)\n", + __func__, dphy_ctx->status); + return; + } + + /*use DPHY_BIT_CLK_SRC_MUX as default clk src*/ + dphy_set_bit_clk_src(dphy_ctx->base_addr, dphy_ctx->clk_src, dphy_ctx->half_pll5); + + /* digital and analog power on */ + dphy_set_power(dphy_ctx->base_addr, true); + + /* turn on DSI continuous clock for HS */ + dphy_set_cont_clk(dphy_ctx->base_addr, true); + + /* set dphy */ + dphy_set_timing(dphy_ctx); + + /* enable data lanes */ + dphy_set_lane_num(dphy_ctx->base_addr, dphy_ctx->lane_num); + + dphy_ctx->status = DPHY_STATUS_INIT; +} + +/** + * spacemit_dphy_core_uninit - unint spacemit dphy + * + * @dphy_ctx: pointer to the spacemit_dphy_ctx + * + * This function will be called by the dsi driver in order to unint the dphy + * This function will disable continous clk, reset dphy, power down dphy + * + * This function has no return value. + * + */ +void spacemit_dphy_core_uninit(struct spacemit_dphy_ctx *dphy_ctx) +{ + pr_debug("%s\n", __func__); + + if(NULL == dphy_ctx){ + pr_err("%s: Invalid param\n", __func__); + return; + } + + if(DPHY_STATUS_INIT != dphy_ctx->status){ + pr_warn("%s: dphy_ctx has not been initialized (%d)\n", + __func__, dphy_ctx->status); + return; + } + + dphy_set_cont_clk(dphy_ctx->base_addr, false); + dphy_ana_reset(dphy_ctx->base_addr); + dphy_set_power(dphy_ctx->base_addr, false); + + dphy_ctx->status = DPHY_STATUS_UNINIT; +} + +int spacemit_dphy_core_parse_dt(struct spacemit_dphy_ctx *dphy_ctx, struct device_node *np) +{ + if (!dphy_ctx) { + pr_err("%s: Param is NULL\n",__func__); + return -1; + } + + dphy_get_setting(dphy_ctx, np); + + return 0; +} + + +static struct dphy_core_ops dphy_core_ops = { + .parse_dt = spacemit_dphy_core_parse_dt, + .init = spacemit_dphy_core_init, + .uninit = spacemit_dphy_core_uninit, + .reset = spacemit_dphy_core_reset, + .get_status = spacemit_dphy_core_get_status, +}; + +static struct ops_entry entry = { + .ver = "spacemit-dphy", + .ops = &dphy_core_ops, +}; + +static int __init dphy_core_register(void) +{ + return dphy_core_ops_register(&entry); +} + +subsys_initcall(dphy_core_register); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_debug.c b/drivers/gpu/drm/spacemit/dpu/dpu_debug.c new file mode 100644 index 000000000000..d3e262cc85b6 --- /dev/null +++ b/drivers/gpu/drm/spacemit/dpu/dpu_debug.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Spacemit Co., Ltd. + * + */ + +#include +#include +#include +#include +#include "dpu_debug.h" +#include "dpu_trace.h" +#include "./../spacemit_dpu_reg.h" +#include "./../spacemit_drm.h" + +dpu_reg_enum SATURN_LE_DPU_REG_ENUM_LISTS[] = { + E_DPU_TOP_REG, + E_DPU_CTRL_REG, + E_DPU_CTRL_REG, + E_DPU_CMDLIST_REG, + E_DPU_INT_REG, + E_DMA_TOP_CTRL_REG, + E_RDMA_LAYER0_REG, + E_RDMA_LAYER1_REG, + E_RDMA_LAYER2_REG, + E_RDMA_LAYER3_REG, + E_MMU_TBU0_REG, + E_MMU_TBU2_REG, + E_MMU_TBU4_REG, + E_MMU_TBU6_REG, + E_COMPOSER2_REG, + E_SCALER0_REG, + E_OUTCTRL2_REG +}; + +static dpu_reg_dump_t dpu_reg_dump_array[] = { + {E_DPU_TOP_REG, "DPU_TOP", DPU_TOP_BASE_ADDR, 218}, + + {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR, 5}, + {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR + 0x24, 8}, + {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR + 0x54, 8}, + {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR + 0x84, 19}, + {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR + 0xe4, 8}, + {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR + 0x114, 25}, + + {E_DPU_CRG_REG, "DPU_CRG", DPU_CRG_BASE_ADDR, 5}, + + {E_DPU_CMDLIST_REG, "DPU_CMDLIST", CMDLIST_BASE_ADDR, 44}, + + {E_DPU_INT_REG, "DPU_INT", DPU_INT_BASE_ADDR, 40}, + + {E_DMA_TOP_CTRL_REG, "DMA_TOP_CTRL", DMA_TOP_BASE_ADDR, 25}, + + {E_RDMA_LAYER0_REG, "RDMA_LAYER0", RDMA0_BASE_ADDR, 31}, + {E_RDMA_LAYER0_REG, "RDMA_LAYER0", RDMA0_BASE_ADDR + 0x80, 57}, + + {E_RDMA_LAYER1_REG, "RDMA_LAYER1", RDMA1_BASE_ADDR, 31}, + {E_RDMA_LAYER1_REG, "RDMA_LAYER1", RDMA1_BASE_ADDR + 0x80, 57}, + + {E_RDMA_LAYER2_REG, "RDMA_LAYER2", RDMA2_BASE_ADDR, 31}, + {E_RDMA_LAYER2_REG, "RDMA_LAYER2", RDMA2_BASE_ADDR + 0x80, 57}, + + {E_RDMA_LAYER3_REG, "RDMA_LAYER3" ,RDMA3_BASE_ADDR, 31}, + {E_RDMA_LAYER3_REG, "RDMA_LAYER3", RDMA3_BASE_ADDR + 0x80, 57}, + + {E_RDMA_LAYER4_REG, "RDMA_LAYER4", RDMA4_BASE_ADDR, 46}, + + {E_RDMA_LAYER5_REG, "RDMA_LAYER5", RDMA5_BASE_ADDR, 46}, + + {E_RDMA_LAYER6_REG, "RDMA_LAYER6", RDMA6_BASE_ADDR, 46}, + + {E_RDMA_LAYER7_REG, "RDMA_LAYER7", RDMA7_BASE_ADDR, 46}, + + {E_RDMA_LAYER8_REG, "RDMA_LAYER8", RDMA8_BASE_ADDR, 46}, + + {E_RDMA_LAYER9_REG, "RDMA_LAYER9", RDMA9_BASE_ADDR, 46}, + + {E_RDMA_LAYER10_REG, "RDMA_LAYER10", RDMA10_BASE_ADDR, 46}, + + {E_RDMA_LAYER11_REG, "RDMA_LAYER11", RDMA11_BASE_ADDR, 46}, + + {E_MMU_TBU0_REG, "MMU_TBU0", MMU_TBU_BASE_ADDR + 0 * MMU_TBU_SIZE, 13}, + {E_MMU_TBU1_REG, "MMU_TBU1", MMU_TBU_BASE_ADDR + 1 * MMU_TBU_SIZE, 13}, + {E_MMU_TBU2_REG, "MMU_TBU2", MMU_TBU_BASE_ADDR + 2 * MMU_TBU_SIZE, 13}, + {E_MMU_TBU3_REG, "MMU_TBU3", MMU_TBU_BASE_ADDR + 3 * MMU_TBU_SIZE, 13}, + {E_MMU_TBU4_REG, "MMU_TBU4", MMU_TBU_BASE_ADDR + 4 * MMU_TBU_SIZE, 13}, + {E_MMU_TBU5_REG, "MMU_TBU5", MMU_TBU_BASE_ADDR + 5 * MMU_TBU_SIZE, 13}, + {E_MMU_TBU6_REG, "MMU_TBU6", MMU_TBU_BASE_ADDR + 6 * MMU_TBU_SIZE, 13}, + {E_MMU_TBU7_REG, "MMU_TBU7", MMU_TBU_BASE_ADDR + 7 * MMU_TBU_SIZE, 13}, + {E_MMU_TBU8_REG, "MMU_TBU8", MMU_TBU_BASE_ADDR + 8 * MMU_TBU_SIZE, 13}, + {E_MMU_TOP_REG, "MMU_TOP", MMU_TOP_BASE_ADDR, 13}, + + {E_LP0_REG, "LP0", LP0_BASE_ADDR, 81}, + {E_LP1_REG, "LP1", LP1_BASE_ADDR, 81}, + {E_LP2_REG, "LP2", LP2_BASE_ADDR, 81}, + {E_LP3_REG, "LP3", LP3_BASE_ADDR, 81}, + {E_LP4_REG, "LP4", LP4_BASE_ADDR, 81}, + {E_LP5_REG, "LP5", LP5_BASE_ADDR, 81}, + {E_LP6_REG, "LP6", LP6_BASE_ADDR, 81}, + {E_LP7_REG, "LP7", LP7_BASE_ADDR, 81}, + {E_LP8_REG, "LP8", LP8_BASE_ADDR, 81}, + {E_LP9_REG, "LP9", LP9_BASE_ADDR, 81}, + {E_LP10_REG, "LP10", LP10_BASE_ADDR, 81}, + {E_LP11_REG, "LP11", LP11_BASE_ADDR, 81}, + + {E_LM0_REG, "LMERGE0", LMERGE0_BASE_ADDR, 4}, + {E_LM1_REG, "LMERGE1", LMERGE1_BASE_ADDR, 4}, + {E_LM2_REG, "LMERGE2", LMERGE2_BASE_ADDR, 4}, + {E_LM3_REG, "LMERGE3", LMERGE3_BASE_ADDR, 4}, + {E_LM4_REG, "LMERGE4", LMERGE4_BASE_ADDR, 4}, + {E_LM5_REG, "LMERGE5", LMERGE5_BASE_ADDR, 4}, + {E_LM6_REG, "LMERGE6", LMERGE6_BASE_ADDR, 4}, + {E_LM7_REG, "LMERGE7", LMERGE7_BASE_ADDR, 4}, + {E_LM8_REG, "LMERGE8", LMERGE8_BASE_ADDR, 4}, + {E_LM9_REG, "LMERGE9", LMERGE9_BASE_ADDR, 4}, + {E_LM10_REG, "LMERGE10", LMERGE10_BASE_ADDR, 4}, + {E_LM11_REG, "LMERGE11", LMERGE11_BASE_ADDR, 4}, + + {E_COMPOSER0_REG, "COMPOSER0", CMP0_BASE_ADDR, 146}, + {E_COMPOSER1_REG, "COMPOSER1", CMP1_BASE_ADDR, 146}, + {E_COMPOSER2_REG, "COMPOSER2", CMP2_BASE_ADDR, 146}, + {E_COMPOSER3_REG, "COMPOSER3", CMP3_BASE_ADDR, 146}, + + {E_SCALER0_REG, "SCALER0", SCALER0_ONLINE_BASE_ADDR, 121}, + {E_SCALER1_REG, "SCALER1", SCALER1_ONLINE_BASE_ADDR, 121}, + + {E_OUTCTRL0_REG, "OUTCTRL0", OUTCTRL0_BASE_ADDR, 55}, + {E_PP0_REG, "PP0", PP0_BASE_ADDR, 86}, + {E_OUTCTRL1_REG, "OUTCTRL1", OUTCTRL1_BASE_ADDR, 55}, + {E_PP1_REG, "PP1", PP1_BASE_ADDR, 86}, + {E_OUTCTRL2_REG, "OUTCTRL2", OUTCTRL2_BASE_ADDR, 55}, + {E_PP2_REG, "PP2", PP2_BASE_ADDR, 86}, + {E_OUTCTRL3_REG, "OUTCTRL3", OUTCTRL3_BASE_ADDR, 55}, + {E_PP3_REG, "PP3", PP3_BASE_ADDR, 86}, + + {E_WB_TOP_0_REG, "WB_TOP_0", WB0_TOP_BASE_ADDR, 54}, + {E_WB_TOP_1_REG, "WB_TOP_1", WB1_TOP_BASE_ADDR, 54}, +}; + +static void dump_dpu_regs_by_enum(void __iomem *io_base, phys_addr_t phy_base, dpu_reg_enum reg_enum, u8 trace_dump) +{ + int i; + int j; + uint32_t reg_num; + void __iomem *io_addr; + phys_addr_t phy_addr; + + dpu_reg_dump_t *tmp = &dpu_reg_dump_array[0]; + + for (i = 0; i < ARRAY_SIZE(dpu_reg_dump_array); i++) { + if (tmp->index == reg_enum) { + reg_num = tmp->dump_reg_num; + io_addr = io_base + tmp->module_offset; + phy_addr = phy_base + tmp->module_offset; + if (trace_dump) { + trace_dpu_reg_info(tmp->module_name, phy_addr, reg_num); + for (j = 0; j < reg_num; j++) { + trace_dpu_reg_dump(phy_addr + j * 4, readl(io_addr + j * 4)); + } + } else { + printk(KERN_DEBUG "%d-%s, address:0x%08llx, num:%d\n", tmp->index, tmp->module_name, phy_addr, reg_num); + for (j = 0; j < reg_num; j++) { + printk(KERN_DEBUG "0x%08llx: 0x%08x\n", (phy_addr + j * 4), readl(io_addr + j * 4)); + } + } + } + tmp++; + } +} + +bool dpu_reg_enum_valid(dpu_reg_enum reg_enum) +{ + int size = ARRAY_SIZE(SATURN_LE_DPU_REG_ENUM_LISTS); + int i = 0; + + for (i = 0; i < size; i++) { + if (reg_enum == SATURN_LE_DPU_REG_ENUM_LISTS[i]) + return true; + } + + return false; +} + +void dump_dpu_regs(struct spacemit_dpu *dpu, dpu_reg_enum reg_enum, u8 trace_dump) +{ + dpu_reg_enum tmp = E_DPU_TOP_REG; + struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private; + struct spacemit_hw_device *hwdev = priv->hwdev; + void __iomem* reg_io_base = hwdev->base; + phys_addr_t reg_phy_base = hwdev->phy_addr; + + if (reg_enum > E_DPU_DUMP_ALL) { + pr_err("invalid dump regsiter enum\n"); + return; + } else if (reg_enum == E_DPU_DUMP_ALL) { + for (; tmp < E_DPU_DUMP_ALL; tmp++) { + if (dpu_reg_enum_valid(tmp)) + dump_dpu_regs_by_enum(reg_io_base, reg_phy_base, tmp, trace_dump); + } + } else { + dump_dpu_regs_by_enum(reg_io_base, reg_phy_base, reg_enum, trace_dump); + } +} + +static void dpu_debug_mode(struct spacemit_hw_device *hwdev, int pipeline_id, bool enable) +{ + u32 base = DPU_CTRL_BASE_ADDR; + + switch (pipeline_id) { + case ONLINE0: + dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl0_dbg_mod, enable ? 1 : 0); + break; + case ONLINE1: + dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl1_dbg_mod, enable ? 1 : 0); + break; + case ONLINE2: + dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl2_dbg_mod, enable ? 1 : 0); + break; + case OFFLINE0: + case OFFLINE1: + default: + DRM_ERROR("pipeline id is invalid!\n"); + break; + } +} +#if IS_ENABLED(CONFIG_GKI_FIX_WORKAROUND) +static struct file *gki_filp_open(const char *filename, int flags, umode_t mode) +{ + return 0; +} +static ssize_t gki_kernel_write(struct file *file, const void *buf, size_t count, + loff_t *pos) +{ + return 0; +} +#endif +#define DPU_BUFFER_DUMP_FILE "/mnt/dpu_buffer_dump" +int dpu_buffer_dump(struct drm_plane *plane) { + unsigned int buffer_size = 0; + int i = 0; + void *mmu_tbl_vaddr = NULL; + phys_addr_t dpu_buffer_paddr = 0; + void __iomem *dpu_buffer_vaddr = NULL; + loff_t pos = 0; + static int dump_once = true; + struct file *filep = NULL; + struct spacemit_plane_state *spacemit_pstate = to_spacemit_plane_state(plane->state); + + if (!dump_once) + return 0; + + mmu_tbl_vaddr = spacemit_pstate->mmu_tbl.va; + buffer_size = plane->state->fb->obj[0]->size >> PAGE_SHIFT; + +#if IS_ENABLED(CONFIG_GKI_FIX_WORKAROUND) + filep = gki_filp_open(DPU_BUFFER_DUMP_FILE, O_RDWR | O_APPEND | O_CREAT, 0644); +#else + filep = filp_open(DPU_BUFFER_DUMP_FILE, O_RDWR | O_APPEND | O_CREAT, 0644); +#endif + + if (IS_ERR(filep)) { + printk("Open file %s error\n", DPU_BUFFER_DUMP_FILE); + return -EINVAL; + } + for (i = 0; i < buffer_size; i++) { + dpu_buffer_paddr = *(volatile u32 __force *)mmu_tbl_vaddr; + dpu_buffer_paddr = dpu_buffer_paddr << PAGE_SHIFT; + if (dpu_buffer_paddr >= 0x80000000UL) { + dpu_buffer_paddr += 0x80000000UL; + } + dpu_buffer_vaddr = phys_to_virt((unsigned long)dpu_buffer_paddr); + mmu_tbl_vaddr += 4; +#if IS_ENABLED(CONFIG_GKI_FIX_WORKAROUND) + gki_kernel_write(filep, (void *)dpu_buffer_vaddr, PAGE_SIZE, &pos); +#else + kernel_write(filep, (void *)dpu_buffer_vaddr, PAGE_SIZE, &pos); +#endif + } + + filp_close(filep, NULL); + filep = NULL; + + dump_once = false; + + return 0; +} + +void dpu_dump_reg(struct spacemit_dpu *dpu) +{ + struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private; + struct spacemit_hw_device *hwdev = priv->hwdev; + + if (!dpu->enable_dump_reg) + return; + + dpu_debug_mode(hwdev, ONLINE2, 1); + dump_dpu_regs(dpu, E_DPU_DUMP_ALL, 1); + dpu_debug_mode(hwdev, ONLINE2, 0); +} + +void dpu_dump_fps(struct spacemit_dpu *dpu) +{ + struct timespec64 cur_tm, tmp_tm; + + if (!dpu->enable_dump_fps) + return; + + ktime_get_real_ts64(&cur_tm); + tmp_tm = timespec64_sub(cur_tm, dpu->last_tm); + dpu->last_tm.tv_sec = cur_tm.tv_sec; + dpu->last_tm.tv_nsec = cur_tm.tv_nsec; + if (tmp_tm.tv_sec == 0) + trace_printk("fps: %ld\n", 1000000000 / (tmp_tm.tv_nsec / 1000)); +} + +void dpu_underrun_wq_stop_trace(struct work_struct *work) +{ +// #ifndef MODULE +// trace_set_clr_event("dpu", NULL, false); +// #endif +} diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_debug.h b/drivers/gpu/drm/spacemit/dpu/dpu_debug.h new file mode 100644 index 000000000000..7aebb50d25a1 --- /dev/null +++ b/drivers/gpu/drm/spacemit/dpu/dpu_debug.h @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Spacemit Co., Ltd. + * + */ + +#ifndef _DPU_DEBUG_H_ +#define _DPU_DEBUG_H_ + +#include +#include "saturn_regs/reg_map.h" +#include "./../spacemit_dpu.h" + +typedef enum { + E_DPU_TOP_REG = 0, + E_DPU_CTRL_REG, + E_DPU_CRG_REG, + E_DPU_CMDLIST_REG, + E_DPU_INT_REG, + + E_DMA_TOP_CTRL_REG, + E_RDMA_LAYER0_REG, + E_RDMA_LAYER1_REG, + E_RDMA_LAYER2_REG, + E_RDMA_LAYER3_REG, + E_RDMA_LAYER4_REG, + E_RDMA_LAYER5_REG, + E_RDMA_LAYER6_REG, + E_RDMA_LAYER7_REG, + E_RDMA_LAYER8_REG, + E_RDMA_LAYER9_REG, + E_RDMA_LAYER10_REG, + E_RDMA_LAYER11_REG, + + E_MMU_TBU0_REG, + E_MMU_TBU1_REG, + E_MMU_TBU2_REG, + E_MMU_TBU3_REG, + E_MMU_TBU4_REG, + E_MMU_TBU5_REG, + E_MMU_TBU6_REG, + E_MMU_TBU7_REG, + E_MMU_TBU8_REG, + E_MMU_TOP_REG, + + E_LP0_REG, + E_LP1_REG, + E_LP2_REG, + E_LP3_REG, + E_LP4_REG, + E_LP5_REG, + E_LP6_REG, + E_LP7_REG, + E_LP8_REG, + E_LP9_REG, + E_LP10_REG, + E_LP11_REG, + + E_LM0_REG, + E_LM1_REG, + E_LM2_REG, + E_LM3_REG, + E_LM4_REG, + E_LM5_REG, + E_LM6_REG, + E_LM7_REG, + E_LM8_REG, + E_LM9_REG, + E_LM10_REG, + E_LM11_REG, + + E_COMPOSER0_REG, + E_COMPOSER1_REG, + E_COMPOSER2_REG, + E_COMPOSER3_REG, + + E_SCALER0_REG, + E_SCALER1_REG, + + E_OUTCTRL0_REG, + E_PP0_REG, + E_OUTCTRL1_REG, + E_PP1_REG, + E_OUTCTRL2_REG, + E_PP2_REG, + E_OUTCTRL3_REG, + E_PP3_REG, + + E_WB_TOP_0_REG, + E_WB_TOP_1_REG, + + E_DPU_DUMP_ALL +}dpu_reg_enum; + +typedef struct dpu_reg_dump { + dpu_reg_enum index; + u8* module_name; + uint32_t module_offset; + uint32_t dump_reg_num; +}dpu_reg_dump_t; + +void dump_dpu_regs(struct spacemit_dpu *dpu, dpu_reg_enum reg_enum, u8 trace_dump); +void dpu_dump_reg(struct spacemit_dpu *dpu); +void dpu_dump_fps(struct spacemit_dpu *dpu); +int dpu_buffer_dump(struct drm_plane *plane); +void dpu_underrun_wq_stop_trace(struct work_struct *work); + +#endif diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_saturn.c b/drivers/gpu/drm/spacemit/dpu/dpu_saturn.c new file mode 100644 index 000000000000..a77ad3d2837d --- /dev/null +++ b/drivers/gpu/drm/spacemit/dpu/dpu_saturn.c @@ -0,0 +1,1860 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Spacemit Co., Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dpu_saturn.h" +#include "saturn_fbcmem.h" +#include "../spacemit_cmdlist.h" +#include "../spacemit_dmmu.h" +#include "../spacemit_dpu_reg.h" +#include "../spacemit_drm.h" +#include "../spacemit_wb.h" +#include