* [PATCH 2/3] drm/bridge: dw-mipi-dsi: add PHY write function
2026-10-30 7:47 [PATCH 0/3] drm/bridge: add glue driver for T-Head TH1520 DW MIPI DSI Icenowy Zheng
2026-10-30 7:47 ` [PATCH 1/3] dt-bindings: display: bridge: document TH1520 MIPI DSI controller Icenowy Zheng
@ 2026-10-30 7:47 ` Icenowy Zheng
2026-10-30 7:56 ` sashiko-bot
2026-10-30 7:47 ` [PATCH 3/3] drm/bridge: add TH1520 DesignWare MIPI DSI host controller Icenowy Zheng
2 siblings, 1 reply; 6+ messages in thread
From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang,
Heiko Stübner, Andy Yan
Cc: dri-devel, devicetree, linux-kernel, linux-rockchip,
linux-arm-kernel, linux-riscv, Icneowy Zheng, Han Gao,
Icenowy Zheng
The dw_mipi_dsi_phy_write() function in the rockchip glue code only
accesses registers defined in the DSI host controller, and the same
process is also present in the kirin dw_drm_dsi driver (which isn't
converted to use the common dw-mipi-dsi code yet) and downstream DSI
driver for T-Head TH1520.
Move this function from dw-mipi-dsi-rockchip driver to the common
dw-mipi-dsi code, and change "test code" (which seems to be the name
used for addresses by DW DPHY) to "address" (which seems to be the name
used by the DW DSI host controller).
Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 21 ++++++
drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 86 ++++++++++---------------
include/drm/bridge/dw_mipi_dsi.h | 3 +
3 files changed, 57 insertions(+), 53 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index 5dd475fcaacf8..87f210ba113ee 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -1104,6 +1104,27 @@ static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
.attach = dw_mipi_dsi_bridge_attach,
};
+void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 addr, u8 data)
+{
+ /*
+ * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
+ * is latched internally as the current address. Data is programmed
+ * internally by rising edge on TESTCLK.
+ */
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) |
+ PHY_TESTDIN(addr));
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
+ PHY_TESTDIN(data));
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+}
+EXPORT_SYMBOL_GPL(dw_mipi_dsi_phy_write);
+
#ifdef CONFIG_DEBUG_FS
static int dw_mipi_dsi_debugfs_write(void *data, u64 val)
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 268d4993a04fd..469123620f302 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -379,28 +379,6 @@ static inline void dsi_write(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 val)
writel(val, dsi->base + reg);
}
-static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi_rockchip *dsi,
- u8 test_code,
- u8 test_data)
-{
- /*
- * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
- * is latched internally as the current test code. Test data is
- * programmed internally by rising edge on TESTCLK.
- */
- dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
-
- dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) |
- PHY_TESTDIN(test_code));
-
- dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
-
- dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
- PHY_TESTDIN(test_data));
-
- dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
-}
-
/*
* ns2bc - Nanoseconds to byte clock cycles
*/
@@ -420,6 +398,7 @@ static inline unsigned int ns2ui(struct dw_mipi_dsi_rockchip *dsi, int ns)
static int dw_mipi_dsi_phy_init(void *priv_data)
{
struct dw_mipi_dsi_rockchip *dsi = priv_data;
+ struct dw_mipi_dsi *dmd = dsi->dmd;
int ret, i, vco;
if (dsi->phy)
@@ -453,24 +432,24 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
return ret;
}
- dw_mipi_dsi_phy_write(dsi, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL,
BYPASS_VCO_RANGE |
VCO_RANGE_CON_SEL(vco) |
VCO_IN_CAP_CON_LOW |
REF_BIAS_CUR_SEL);
- dw_mipi_dsi_phy_write(dsi, PLL_CP_CONTROL_PLL_LOCK_BYPASS,
+ dw_mipi_dsi_phy_write(dmd, PLL_CP_CONTROL_PLL_LOCK_BYPASS,
CP_CURRENT_SEL(dppa_map[i].icpctrl));
- dw_mipi_dsi_phy_write(dsi, PLL_LPF_AND_CP_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, PLL_LPF_AND_CP_CONTROL,
CP_PROGRAM_EN | LPF_PROGRAM_EN |
LPF_RESISTORS_SEL(dppa_map[i].lpfctrl));
- dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0,
+ dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_0,
HSFREQRANGE_SEL(dppa_map[i].hsfreqrange));
- dw_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO,
+ dw_mipi_dsi_phy_write(dmd, PLL_INPUT_DIVIDER_RATIO,
INPUT_DIVIDER(dsi->input_div));
- dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
+ dw_mipi_dsi_phy_write(dmd, PLL_LOOP_DIVIDER_RATIO,
LOOP_DIV_LOW_SEL(dsi->feedback_div) |
LOW_PROGRAM_EN);
/*
@@ -479,53 +458,53 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
* and lab test results.
* Only in this way can we get correct mipi phy pll frequency.
*/
- dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
- dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
+ dw_mipi_dsi_phy_write(dmd, PLL_LOOP_DIVIDER_RATIO,
LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
HIGH_PROGRAM_EN);
- dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
- dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
+ dw_mipi_dsi_phy_write(dmd, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
LOW_PROGRAM_EN | BIASEXTR_SEL(BIASEXTR_127_7));
- dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
+ dw_mipi_dsi_phy_write(dmd, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
HIGH_PROGRAM_EN | BANDGAP_SEL(BANDGAP_96_10));
- dw_mipi_dsi_phy_write(dsi, BANDGAP_AND_BIAS_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, BANDGAP_AND_BIAS_CONTROL,
POWER_CONTROL | INTERNAL_REG_CURRENT |
BIAS_BLOCK_ON | BANDGAP_ON);
- dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, TERMINATION_RESISTER_CONTROL,
TER_RESISTOR_LOW | TER_CAL_DONE |
SETRD_MAX | TER_RESISTORS_ON);
- dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, TERMINATION_RESISTER_CONTROL,
TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON |
SETRD_MAX | POWER_MANAGE |
TER_RESISTORS_ON);
- dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL,
TLP_PROGRAM_EN | ns2bc(dsi, 500));
- dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL,
THS_PRE_PROGRAM_EN | ns2ui(dsi, 40));
- dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL,
THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300));
- dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL,
THS_PRE_PROGRAM_EN | ns2ui(dsi, 100));
- dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL,
BIT(5) | ns2bc(dsi, 100));
- dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_POST_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_POST_TIME_CONTROL,
BIT(5) | (ns2bc(dsi, 60) + 7));
- dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL,
TLP_PROGRAM_EN | ns2bc(dsi, 500));
- dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL,
THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 20));
- dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL,
THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2));
- dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL,
THS_PRE_PROGRAM_EN | (ns2ui(dsi, 60) + 8));
- dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL,
+ dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL,
BIT(5) | ns2bc(dsi, 100));
clk_disable_unprepare(dsi->phy_cfg_clk);
@@ -1224,6 +1203,7 @@ static int dw_mipi_dsi_dphy_configure(struct phy *phy, union phy_configure_opts
static int dw_mipi_dsi_dphy_power_on(struct phy *phy)
{
struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy);
+ struct dw_mipi_dsi *dmd = dsi->dmd;
int i, ret;
DRM_DEV_DEBUG(dsi->dev, "lanes %d - data_rate_mbps %u\n",
@@ -1273,15 +1253,15 @@ static int dw_mipi_dsi_dphy_power_on(struct phy *phy)
* Configure hsfreqrange according to frequency values
* Set clock lane and hsfreqrange by lane0(test code 0x44)
*/
- dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_CLK, 0);
- dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0,
+ dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_CLK, 0);
+ dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_0,
HSFREQRANGE_SEL(dppa_map[i].hsfreqrange));
- dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_1, 0);
- dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_2, 0);
- dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_3, 0);
+ dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_1, 0);
+ dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_2, 0);
+ dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_3, 0);
/* Normal operation */
- dw_mipi_dsi_phy_write(dsi, 0x0, 0);
+ dw_mipi_dsi_phy_write(dmd, 0x0, 0);
clk_disable_unprepare(dsi->phy_cfg_clk);
clk_disable_unprepare(dsi->grf_clk);
diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h
index 65d5e68065e3d..16fa3ffd434f1 100644
--- a/include/drm/bridge/dw_mipi_dsi.h
+++ b/include/drm/bridge/dw_mipi_dsi.h
@@ -85,4 +85,7 @@ void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi);
void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave);
struct drm_bridge *dw_mipi_dsi_get_bridge(struct dw_mipi_dsi *dsi);
+/* PHY configuration */
+void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 addr, u8 data);
+
#endif /* __DW_MIPI_DSI__ */
--
2.56.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 3/3] drm/bridge: add TH1520 DesignWare MIPI DSI host controller
2026-10-30 7:47 [PATCH 0/3] drm/bridge: add glue driver for T-Head TH1520 DW MIPI DSI Icenowy Zheng
2026-10-30 7:47 ` [PATCH 1/3] dt-bindings: display: bridge: document TH1520 MIPI DSI controller Icenowy Zheng
2026-10-30 7:47 ` [PATCH 2/3] drm/bridge: dw-mipi-dsi: add PHY write function Icenowy Zheng
@ 2026-10-30 7:47 ` Icenowy Zheng
2026-10-30 7:58 ` sashiko-bot
2 siblings, 1 reply; 6+ messages in thread
From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang,
Heiko Stübner, Andy Yan
Cc: dri-devel, devicetree, linux-kernel, linux-rockchip,
linux-arm-kernel, linux-riscv, Icneowy Zheng, Han Gao,
Icenowy Zheng
The T-Head TH1520 SoC contains a Synopsys DesignWare MIPI DSI host
controller with an internal DesignWare MIPI D-PHY.
Add a glue driver configuring clocks and PHY parameters for it.
Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
drivers/gpu/drm/bridge/Kconfig | 10 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c | 530 ++++++++++++++++++++++++++++
3 files changed, 541 insertions(+)
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 4a57d49b4c6d3..b005623ba26bf 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -375,6 +375,16 @@ config DRM_THEAD_TH1520_DW_HDMI
Choose this to enable support for the internal HDMI bridge found
on the T-Head TH1520 SoC.
+config DRM_THEAD_TH1520_DW_MIPI_DSI
+ tristate "T-Head TH1520 DesignWare MIPI DSI bridge"
+ depends on OF
+ depends on COMMON_CLK
+ depends on ARCH_THEAD || COMPILE_TEST
+ select DRM_DW_MIPI_DSI
+ help
+ Choose this to enable support for the internal MIPI DSI bridge
+ found on the T-Head TH1520 SoC.
+
config DRM_TOSHIBA_TC358762
tristate "TC358762 DSI/DPI bridge"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 15cc821d85b7e..5af871ba5ce77 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_DRM_SII9234) += sii9234.o
obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o
obj-$(CONFIG_DRM_SOLOMON_SSD2825) += ssd2825.o
obj-$(CONFIG_DRM_THEAD_TH1520_DW_HDMI) += th1520-dw-hdmi.o
+obj-$(CONFIG_DRM_THEAD_TH1520_DW_MIPI_DSI) += th1520-dw-mipi-dsi.o
obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o
obj-$(CONFIG_DRM_TOSHIBA_TC358762) += tc358762.o
obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o
diff --git a/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c b/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c
new file mode 100644
index 0000000000000..e5c7c5d6dcc71
--- /dev/null
+++ b/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c
@@ -0,0 +1,530 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS)
+ *
+ * Author:
+ * Icenowy Zheng <zhengxingda@iscas.ac.cn>
+ *
+ * Based on dw_mipi_dsi-stm.c, which is:
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Based on dw-mipi-dsi-rockchip.c, which is:
+ * Copyright (C) Rockchip Electronics Co., Ltd.
+ *
+ * Based on vendor kernel phy-dw-mipi-dphy.c, which is:
+ * Copyright (C) Alibaba Group Holding Limited.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/bridge/dw_mipi_dsi.h>
+#include <drm/drm_mipi_dsi.h>
+
+/* DPHY registers */
+
+#define PLL_DELAY_LP_TX_START_LP11 0x02
+#define TXDINDLYTIME_9_2 GENMASK(7, 0)
+
+#define PLL_FSM_CNTRL 0x03
+
+#define PLL_PROP_CHARGE_PUMP_CTRL 0x0e
+#define PLL_PROP_CNTRL GENMASK(5, 0)
+
+#define PLL_INT_CHARGE_PUMP_CTRL 0x0f
+#define PLL_INT_CNTRL GENMASK(5, 0)
+
+#define PLL_VCO_CTRL 0x12
+#define PLL_VCO_CNTRL_OVR_EN BIT(6)
+#define PLL_VCO_CNTRL_OVR GENMASK(5, 0)
+
+#define PLL_GMP_CTRL_DIGITAL_TEST 0x13
+#define PLL_TESTLOCK BIT(7)
+#define PLL_GMP_CNTRL GENMASK(5, 4)
+#define TSTPLLDIG GENMASK(3, 1)
+#define TSTPLLDIG_LOCK (0x0 << 1)
+#define TSTPLLDIG_REFCLK (0x1 << 1)
+#define TSTPLLDIG_FBCLK (0x2 << 1)
+#define TSTPLLDIG_INT_CLKIN (0x3 << 1)
+#define TSTPLLDIG_LOCK_FROM_DETECTOR (0x4 << 1)
+#define TSTPLLDIG_OUTPUT (0x5 << 1)
+#define TSTPLLDIG_BUF_VCOCLK (0x6 << 1)
+#define TSTPLLDIG_UPDATEPLL (0x7 << 1)
+
+#define PLL_N_DIV_FSM_SIG 0x17
+#define PLL_N_OVR GENMASK(3, 0)
+
+#define PLL_M_DIV 0x18
+#define PLL_M_OVR_4_0 GENMASK(4, 0)
+#define PLL_M_OVR_9_5 GENMASK(4, 0)
+
+#define PLL_N_AND_M_DIV_CTRL 0x19
+#define PLL_M_OVR_EN BIT(5)
+#define PLL_N_OVR_EN BIT(4)
+
+#define PLL_CHARGE_PUMP_BIAS_CTRL 0x1c
+#define PLL_CPBIAS_CNTRL GENMASK(6, 0)
+
+#define PLL_ANALOG_PROGRAM_CONTROL 0x1f
+#define PLL_MPLL_PROG_5_0 GENMASK(5, 0)
+#define PLL_MPLL_PROG_11_6 GENMASK(5, 0)
+#define PLL_MPLL_PROG_16_12 GENMASK(4, 0)
+
+#define HS_FREQ_RANGE_OF_OPERATION 0x44
+#define HSFREQRANGE_OVR_EN BIT(7)
+#define HSFREQRANGE_OVR GENMASK(6, 0)
+#define HSFREQRANGE HSFREQRANGE_OVR
+
+#define PG_LP_BIAS_LANE0 0x4a
+#define PRG_ON_LANE0 BIT(6)
+
+#define SR_FSM_OVR_CNTRL 0xa0
+#define SR_DDL_LOOP_CONF 0xa3
+
+/* syscon registers */
+
+#define MIPIDSI0_SYSREG 0x8
+#define MIPIDSI1_SYSREG 0xc
+#define MIPIDSI_HSFREQRANGE GENMASK(9, 3)
+#define MIPIDSI_CFGCLKFREQRANGE GENMASK(15, 10)
+#define MIPIDSI_CFGCLKFREQRANGE_DEF (0x1c << 10)
+
+struct pll_hsfreqrange {
+ unsigned int max_mbps;
+ u8 hsfreqrange;
+};
+
+static const struct pll_hsfreqrange hsfreqrange_table[] = {
+ { 80, 0x00 },
+ { 90, 0x10 },
+ { 100, 0x20 },
+ { 110, 0x30 },
+ { 120, 0x01 },
+ { 130, 0x11 },
+ { 140, 0x21 },
+ { 150, 0x31 },
+ { 160, 0x02 },
+ { 170, 0x12 },
+ { 180, 0x22 },
+ { 190, 0x32 },
+ { 205, 0x03 },
+ { 220, 0x13 },
+ { 235, 0x23 },
+ { 250, 0x33 },
+ { 275, 0x04 },
+ { 300, 0x14 },
+ { 325, 0x25 },
+ { 350, 0x35 },
+ { 400, 0x05 },
+ { 450, 0x16 },
+ { 500, 0x26 },
+ { 550, 0x37 },
+ { 600, 0x07 },
+ { 650, 0x18 },
+ { 700, 0x28 },
+ { 750, 0x39 },
+ { 800, 0x09 },
+ { 850, 0x19 },
+ { 900, 0x29 },
+ { 950, 0x3A },
+ { 1000, 0x0A },
+ { 1050, 0x1A },
+ { 1100, 0x2A },
+ { 1150, 0x3B },
+ { 1200, 0x0B },
+ { 1250, 0x1B },
+ { 1300, 0x2B },
+ { 1350, 0x3C },
+ { 1400, 0x0C },
+ { 1450, 0x1C },
+ { 1500, 0x2C },
+ { 1550, 0x3D },
+ { 1600, 0x0D },
+ { 1650, 0x1D },
+ { 1700, 0x2E },
+ { 1750, 0x3E },
+ { 1800, 0x0E },
+ { 1850, 0x1E },
+ { 1900, 0x2F },
+ { 1950, 0x3F },
+ { 2000, 0x0F },
+ { 2050, 0x40 },
+ { 2100, 0x41 },
+ { 2150, 0x42 },
+ { 2200, 0x43 },
+ { 2250, 0x44 },
+ { 2300, 0x45 },
+ { 2350, 0x46 },
+ { 2400, 0x47 },
+ { 2450, 0x48 },
+ { 2500, 0x49 },
+};
+
+struct hstt {
+ unsigned int max_mbps;
+ struct dw_mipi_dsi_dphy_timing timing;
+};
+
+#define HSTT(_max_mbps, _c_lp2hs, _c_hs2lp, _d_lp2hs, _d_hs2lp) \
+{ \
+ .max_mbps = _max_mbps, \
+ .timing = { \
+ .clk_lp2hs = _c_lp2hs, \
+ .clk_hs2lp = _c_hs2lp, \
+ .data_lp2hs = _d_lp2hs, \
+ .data_hs2lp = _d_hs2lp, \
+ } \
+}
+
+static struct hstt hstt_table[] = {
+ HSTT(80, 21, 17, 15, 10),
+ HSTT(90, 23, 17, 16, 10),
+ HSTT(100, 22, 17, 16, 10),
+ HSTT(110, 25, 18, 17, 11),
+ HSTT(120, 26, 20, 18, 11),
+ HSTT(130, 27, 19, 19, 11),
+ HSTT(140, 27, 19, 19, 11),
+ HSTT(150, 28, 20, 20, 12),
+ HSTT(160, 30, 21, 22, 13),
+ HSTT(170, 30, 21, 23, 13),
+ HSTT(180, 31, 21, 23, 13),
+ HSTT(190, 32, 22, 24, 13),
+ HSTT(205, 35, 22, 25, 13),
+ HSTT(220, 37, 26, 27, 15),
+ HSTT(235, 38, 28, 27, 16),
+ HSTT(250, 41, 29, 30, 17),
+ HSTT(275, 43, 29, 32, 18),
+ HSTT(300, 45, 32, 35, 19),
+ HSTT(325, 48, 33, 36, 18),
+ HSTT(350, 51, 35, 40, 20),
+ HSTT(400, 59, 37, 44, 21),
+ HSTT(450, 65, 40, 49, 23),
+ HSTT(500, 71, 41, 54, 24),
+ HSTT(550, 77, 44, 57, 26),
+ HSTT(600, 82, 46, 64, 27),
+ HSTT(650, 87, 48, 67, 28),
+ HSTT(700, 94, 52, 71, 29),
+ HSTT(750, 99, 52, 75, 31),
+ HSTT(800, 105, 55, 82, 32),
+ HSTT(850, 110, 58, 85, 32),
+ HSTT(900, 115, 58, 88, 35),
+ HSTT(950, 120, 62, 93, 36),
+ HSTT(1000, 128, 63, 99, 38),
+ HSTT(1050, 132, 65, 102, 38),
+ HSTT(1100, 138, 67, 106, 39),
+ HSTT(1150, 146, 69, 112, 42),
+ HSTT(1200, 151, 71, 117, 43),
+ HSTT(1250, 153, 74, 120, 45),
+ HSTT(1300, 160, 73, 124, 46),
+ HSTT(1350, 165, 76, 130, 47),
+ HSTT(1400, 172, 78, 134, 49),
+ HSTT(1450, 177, 80, 138, 49),
+ HSTT(1500, 183, 81, 143, 52),
+ HSTT(1550, 191, 84, 147, 52),
+ HSTT(1600, 194, 85, 152, 52),
+ HSTT(1650, 201, 86, 155, 53),
+ HSTT(1700, 208, 88, 161, 53),
+ HSTT(1750, 212, 89, 165, 53),
+ HSTT(1800, 220, 90, 171, 54),
+ HSTT(1850, 223, 92, 175, 54),
+ HSTT(1900, 231, 91, 180, 55),
+ HSTT(1950, 236, 95, 185, 56),
+ HSTT(2000, 243, 97, 190, 56),
+ HSTT(2050, 248, 99, 194, 58),
+ HSTT(2100, 252, 100, 199, 59),
+ HSTT(2150, 259, 102, 204, 61),
+ HSTT(2200, 266, 105, 210, 62),
+ HSTT(2250, 269, 109, 213, 63),
+ HSTT(2300, 272, 109, 217, 65),
+ HSTT(2350, 281, 112, 225, 66),
+ HSTT(2400, 283, 115, 226, 66),
+ HSTT(2450, 282, 115, 226, 67),
+ HSTT(2500, 281, 118, 227, 67),
+};
+
+struct dsi_chip_data {
+ u64 reg;
+
+ u32 sysreg;
+};
+
+struct th1520_dw_mipi_dsi {
+ struct device *dev;
+
+ struct dw_mipi_dsi *dmd;
+ struct dw_mipi_dsi_plat_data pdata;
+ const struct dsi_chip_data *cdata;
+
+ struct regmap *syscon;
+ struct clk *clk_pix;
+ struct clk *clk_ref;
+ struct clk *clk_cfg;
+
+ unsigned int lane_mbps;
+ u8 hsfreqrange;
+};
+
+static int dsi_phy_init(void *priv_data)
+{
+ struct th1520_dw_mipi_dsi *dsi = priv_data;
+ u32 val;
+
+ val = FIELD_PREP(MIPIDSI_HSFREQRANGE, dsi->hsfreqrange) |
+ MIPIDSI_CFGCLKFREQRANGE_DEF;
+
+ regmap_update_bits(dsi->syscon, dsi->cdata->sysreg,
+ MIPIDSI_HSFREQRANGE | MIPIDSI_CFGCLKFREQRANGE, val);
+
+ /* disable slew rate calibration */
+ dw_mipi_dsi_phy_write(dsi->dmd, SR_DDL_LOOP_CONF, 0);
+ dw_mipi_dsi_phy_write(dsi->dmd, SR_FSM_OVR_CNTRL, 1);
+
+ /* configure prg_on_lane0 */
+ dw_mipi_dsi_phy_write(dsi->dmd, PG_LP_BIAS_LANE0, PRG_ON_LANE0);
+
+ /* configure PLL charge pump */
+ if (dsi->lane_mbps >= 1150)
+ dw_mipi_dsi_phy_write(dsi->dmd, PLL_PROP_CHARGE_PUMP_CTRL, 0xE);
+ else
+ dw_mipi_dsi_phy_write(dsi->dmd, PLL_PROP_CHARGE_PUMP_CTRL, 0x8);
+ dw_mipi_dsi_phy_write(dsi->dmd, PLL_CHARGE_PUMP_BIAS_CTRL, 0x10);
+
+ return 0;
+}
+
+static void dsi_phy_power_on(void *priv_data)
+{
+ struct th1520_dw_mipi_dsi *dsi = priv_data;
+
+ clk_prepare_enable(dsi->clk_pix);
+}
+
+static void dsi_phy_power_off(void *priv_data)
+{
+ struct th1520_dw_mipi_dsi *dsi = priv_data;
+
+ clk_disable_unprepare(dsi->clk_pix);
+}
+
+static unsigned int
+calculate_lane_mbps(const struct drm_display_mode *mode,
+ unsigned long mode_flags,
+ u32 lanes, u32 format)
+{
+ int bpp = mipi_dsi_pixel_format_to_bpp(format);
+ union phy_configure_opts phy_opts;
+
+ /*
+ * FIXME: the extra bandwidth headroom logic mimics what the Rockchip
+ * driver does, and test shows that it's necessary for the hardware
+ * to work with the mainline dw-mipi-dsi common code.
+ */
+ phy_mipi_dphy_get_default_config(mode->clock * 1000 * 10 / 8,
+ bpp, lanes,
+ &phy_opts.mipi_dphy);
+
+ return DIV_ROUND_UP(phy_opts.mipi_dphy.hs_clk_rate, USEC_PER_SEC);
+}
+
+static int
+dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
+ unsigned long mode_flags, u32 lanes, u32 format,
+ unsigned int *lane_mbps)
+{
+ struct th1520_dw_mipi_dsi *dsi = priv_data;
+ int i;
+
+ *lane_mbps = calculate_lane_mbps(mode, mode_flags, lanes, format);
+
+ for (i = 0; i < ARRAY_SIZE(hsfreqrange_table); i++)
+ if (*lane_mbps <= hsfreqrange_table[i].max_mbps)
+ break;
+
+ if (i == ARRAY_SIZE(hsfreqrange_table))
+ return -EINVAL;
+
+ *lane_mbps = hsfreqrange_table[i].max_mbps;
+ dsi->lane_mbps = *lane_mbps;
+ dsi->hsfreqrange = hsfreqrange_table[i].hsfreqrange;
+
+ return 0;
+}
+
+static int
+dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps,
+ struct dw_mipi_dsi_dphy_timing *timing)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hstt_table); i++)
+ if (lane_mbps <= hstt_table[i].max_mbps)
+ break;
+
+ if (i == ARRAY_SIZE(hstt_table))
+ return -EINVAL;
+
+ *timing = hstt_table[i].timing;
+
+ return 0;
+}
+
+static const struct dw_mipi_dsi_phy_ops th1520_dw_mipi_dsi_phy_ops = {
+ .init = dsi_phy_init,
+ .power_on = dsi_phy_power_on,
+ .power_off = dsi_phy_power_off,
+ .get_lane_mbps = dsi_get_lane_mbps,
+ .get_timing = dsi_phy_get_timing,
+};
+
+static enum drm_mode_status
+dsi_mode_valid(void *priv_data, const struct drm_display_mode *mode,
+ unsigned long mode_flags, u32 lanes, u32 format)
+{
+ unsigned int lane_mbps, max_mbps;
+
+ lane_mbps = calculate_lane_mbps(mode, mode_flags, lanes, format);
+ max_mbps = hsfreqrange_table[ARRAY_SIZE(hsfreqrange_table) - 1].max_mbps;
+
+ if (lane_mbps > max_mbps)
+ return MODE_NOCLOCK;
+
+ return MODE_OK;
+}
+
+static struct dsi_chip_data th1520_chip_data[] = {
+ {
+ .reg = 0xffef500000,
+ .sysreg = MIPIDSI0_SYSREG,
+
+ /*
+ * TODO: The user manual shows a mux register for DSI0 but the
+ * vendor driver doesn't touch it either.
+ */
+ },
+ {
+ .reg = 0xffef510000,
+ .sysreg = MIPIDSI1_SYSREG,
+ },
+ { /* sentinel */ },
+};
+
+static int th1520_dw_mipi_dsi_probe(struct platform_device *pdev)
+{
+ const struct dsi_chip_data *cdata;
+ struct device *dev = &pdev->dev;
+ struct th1520_dw_mipi_dsi *dsi;
+ struct resource *res;
+ void __iomem *base;
+ int i, ret;
+
+ dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+ if (!dsi)
+ return -ENOMEM;
+
+ dsi->dev = dev;
+
+ dsi->syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
+ if (IS_ERR(dsi->syscon)) {
+ ret = PTR_ERR(dsi->syscon);
+ dev_err_probe(dev, ret, "Unable to get system controller regmap\n");
+ return ret;
+ }
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base)) {
+ dev_err_probe(dev, ret, "Unable to get DSI registers\n");
+ return PTR_ERR(base);
+ }
+
+ cdata = of_device_get_match_data(dev);
+ i = 0;
+ while (cdata[i].reg) {
+ if (cdata[i].reg == res->start) {
+ dsi->cdata = &cdata[i];
+ break;
+ }
+
+ i++;
+ }
+
+ if (!dsi->cdata) {
+ dev_err(dev, "Failed to find chip data for this controller\n");
+ return -EINVAL;
+ }
+
+ dsi->clk_ref = devm_clk_get_enabled(dev, "ref");
+ if (IS_ERR(dsi->clk_ref)) {
+ ret = PTR_ERR(dsi->clk_ref);
+ dev_err_probe(dev, ret, "Unable to get PHY reference clock\n");
+ return ret;
+ }
+
+ dsi->clk_cfg = devm_clk_get_enabled(dev, "phy_cfg");
+ if (IS_ERR(dsi->clk_cfg)) {
+ ret = PTR_ERR(dsi->clk_cfg);
+ dev_err_probe(dev, ret, "Unable to get PHY configuration clock\n");
+ return ret;
+ }
+
+ dsi->clk_pix = devm_clk_get(dev, "pixel");
+ if (IS_ERR(dsi->clk_pix)) {
+ ret = PTR_ERR(dsi->clk_pix);
+ dev_err_probe(dev, ret, "Unable to get PHY pixel clock\n");
+ return ret;
+ }
+
+ dsi->pdata.base = base;
+ dsi->pdata.priv_data = dsi;
+ dsi->pdata.max_data_lanes = 4;
+ dsi->pdata.phy_ops = &th1520_dw_mipi_dsi_phy_ops;
+ dsi->pdata.mode_valid = dsi_mode_valid;
+
+ platform_set_drvdata(pdev, dsi);
+
+ dsi->dmd = dw_mipi_dsi_probe(pdev, &dsi->pdata);
+ if (IS_ERR(dsi->dmd)) {
+ ret = PTR_ERR(dsi->dmd);
+ dev_err_probe(dev, ret, "Failed to initialize mipi dsi host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void th1520_dw_mipi_dsi_remove(struct platform_device *pdev)
+{
+ struct th1520_dw_mipi_dsi *dsi = platform_get_drvdata(pdev);
+
+ dw_mipi_dsi_remove(dsi->dmd);
+}
+
+static const struct of_device_id th1520_dw_mipi_dsi_dt_ids[] = {
+ { .compatible = "thead,th1520-dw-mipi-dsi", .data = &th1520_chip_data },
+ { },
+};
+MODULE_DEVICE_TABLE(of, th1520_dw_mipi_dsi_dt_ids);
+
+static struct platform_driver th1520_dw_mipi_dsi_driver = {
+ .probe = th1520_dw_mipi_dsi_probe,
+ .remove = th1520_dw_mipi_dsi_remove,
+ .driver = {
+ .of_match_table = th1520_dw_mipi_dsi_dt_ids,
+ .name = "th1520-dw-mipi-dsi",
+ },
+};
+
+module_platform_driver(th1520_dw_mipi_dsi_driver);
+
+MODULE_AUTHOR("Icenowy Zheng <zhengxingda@iscas.ac.cn>");
+MODULE_DESCRIPTION("T-Head TH1520 MIPI DSI host controller driver");
+MODULE_LICENSE("GPL");
--
2.56.0
^ permalink raw reply related [flat|nested] 6+ messages in thread