This has been tested against the VOP2 on the RK3566 with a separate
workaround on top following in the next commit.

Signed-off-by: Ahmad Fatoum <[email protected]>
---
 drivers/video/rockchip/Kconfig                |    8 +
 drivers/video/rockchip/Makefile               |    1 +
 drivers/video/rockchip/dw-mipi-dsi-rockchip.c | 1381 +++++++++++++++++
 drivers/video/rockchip/rockchip_drm_vop2.c    |    3 +-
 4 files changed, 1392 insertions(+), 1 deletion(-)
 create mode 100644 drivers/video/rockchip/dw-mipi-dsi-rockchip.c

diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig
index b91c6fc398e0..3453be7a7dfa 100644
--- a/drivers/video/rockchip/Kconfig
+++ b/drivers/video/rockchip/Kconfig
@@ -15,4 +15,12 @@ config DRIVER_VIDEO_ROCKCHIP_HDMI
         select DRIVER_VIDEO_EDID
         select DRIVER_VIDEO_DW_HDMI
 
+config DRIVER_VIDEO_ROCKCHIP_MIPI_DSI
+        bool "Rockchip MIPI-DSI driver"
+        select VIDEO_VPL
+        select OFTREE
+        select DRIVER_VIDEO_DW_MIPI_DSI
+        select GENERIC_PHY
+        select GENERIC_PHY_MIPI_DPHY
+
 endif
diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile
index 278ce1302d92..b7b6267c1d8f 100644
--- a/drivers/video/rockchip/Makefile
+++ b/drivers/video/rockchip/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_DRIVER_VIDEO_ROCKCHIP_VOP2) += rockchip_drm_vop2.o 
rockchip_vop2_reg.o
 obj-$(CONFIG_DRIVER_VIDEO_ROCKCHIP_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_DRIVER_VIDEO_ROCKCHIP_MIPI_DSI) += dw-mipi-dsi-rockchip.o
diff --git a/drivers/video/rockchip/dw-mipi-dsi-rockchip.c 
b/drivers/video/rockchip/dw-mipi-dsi-rockchip.c
new file mode 100644
index 000000000000..f065ebcefa00
--- /dev/null
+++ b/drivers/video/rockchip/dw-mipi-dsi-rockchip.c
@@ -0,0 +1,1381 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Rockchip Electronics Co., Ltd.
+ * Author:
+ *      Chris Zhong <[email protected]>
+ *      Nickey Yang <[email protected]>
+ */
+
+#include <linux/array_size.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/math.h>
+#include <mfd/syscon.h>
+#include <linux/module.h>
+#include <of_device.h>
+#include <of.h>
+#include <of_graph.h>
+#include <linux/phy/phy.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+#include <video/mipi_display.h>
+#include <video/mipi_dsi.h>
+#include <video/dw_mipi_dsi.h>
+#include <video/drm/drm_modes.h>
+
+#include "rockchip_drm_drv.h"
+
+#define DRM_DEV_ERROR dev_err
+#define DRM_DEV_DEBUG dev_dbg
+
+#define DSI_PHY_RSTZ                   0xa0
+#define PHY_DISFORCEPLL                        0
+#define PHY_ENFORCEPLL                 BIT(3)
+#define PHY_DISABLECLK                 0
+#define PHY_ENABLECLK                  BIT(2)
+#define PHY_RSTZ                       0
+#define PHY_UNRSTZ                     BIT(1)
+#define PHY_SHUTDOWNZ                  0
+#define PHY_UNSHUTDOWNZ                        BIT(0)
+
+#define DSI_PHY_IF_CFG                 0xa4
+#define N_LANES(n)                     ((((n) - 1) & 0x3) << 0)
+#define PHY_STOP_WAIT_TIME(cycle)      (((cycle) & 0xff) << 8)
+
+#define DSI_PHY_STATUS                 0xb0
+#define LOCK                           BIT(0)
+#define STOP_STATE_CLK_LANE            BIT(2)
+
+#define DSI_PHY_TST_CTRL0              0xb4
+#define PHY_TESTCLK                    BIT(1)
+#define PHY_UNTESTCLK                  0
+#define PHY_TESTCLR                    BIT(0)
+#define PHY_UNTESTCLR                  0
+
+#define DSI_PHY_TST_CTRL1              0xb8
+#define PHY_TESTEN                     BIT(16)
+#define PHY_UNTESTEN                   0
+#define PHY_TESTDOUT(n)                        (((n) & 0xff) << 8)
+#define PHY_TESTDIN(n)                 (((n) & 0xff) << 0)
+
+#define DSI_INT_ST0                    0xbc
+#define DSI_INT_ST1                    0xc0
+#define DSI_INT_MSK0                   0xc4
+#define DSI_INT_MSK1                   0xc8
+
+#define PHY_STATUS_TIMEOUT_US          10000
+#define CMD_PKT_STATUS_TIMEOUT_US      20000
+
+#define BYPASS_VCO_RANGE       BIT(7)
+#define VCO_RANGE_CON_SEL(val) (((val) & 0x7) << 3)
+#define VCO_IN_CAP_CON_DEFAULT (0x0 << 1)
+#define VCO_IN_CAP_CON_LOW     (0x1 << 1)
+#define VCO_IN_CAP_CON_HIGH    (0x2 << 1)
+#define REF_BIAS_CUR_SEL       BIT(0)
+
+#define CP_CURRENT_3UA 0x1
+#define CP_CURRENT_4_5UA       0x2
+#define CP_CURRENT_7_5UA       0x6
+#define CP_CURRENT_6UA 0x9
+#define CP_CURRENT_12UA        0xb
+#define CP_CURRENT_SEL(val)    ((val) & 0xf)
+#define CP_PROGRAM_EN          BIT(7)
+
+#define LPF_RESISTORS_15_5KOHM 0x1
+#define LPF_RESISTORS_13KOHM   0x2
+#define LPF_RESISTORS_11_5KOHM 0x4
+#define LPF_RESISTORS_10_5KOHM 0x8
+#define LPF_RESISTORS_8KOHM    0x10
+#define LPF_PROGRAM_EN         BIT(6)
+#define LPF_RESISTORS_SEL(val) ((val) & 0x3f)
+
+#define HSFREQRANGE_SEL(val)   (((val) & 0x3f) << 1)
+
+#define INPUT_DIVIDER(val)     (((val) - 1) & 0x7f)
+#define LOW_PROGRAM_EN         0
+#define HIGH_PROGRAM_EN                BIT(7)
+#define LOOP_DIV_LOW_SEL(val)  (((val) - 1) & 0x1f)
+#define LOOP_DIV_HIGH_SEL(val) ((((val) - 1) >> 5) & 0xf)
+#define PLL_LOOP_DIV_EN                BIT(5)
+#define PLL_INPUT_DIV_EN       BIT(4)
+
+#define POWER_CONTROL          BIT(6)
+#define INTERNAL_REG_CURRENT   BIT(3)
+#define BIAS_BLOCK_ON          BIT(2)
+#define BANDGAP_ON             BIT(0)
+
+#define TER_RESISTOR_HIGH      BIT(7)
+#define        TER_RESISTOR_LOW        0
+#define LEVEL_SHIFTERS_ON      BIT(6)
+#define TER_CAL_DONE           BIT(5)
+#define SETRD_MAX              (0x7 << 2)
+#define POWER_MANAGE           BIT(1)
+#define TER_RESISTORS_ON       BIT(0)
+
+#define BIASEXTR_SEL(val)      ((val) & 0x7)
+#define BANDGAP_SEL(val)       ((val) & 0x7)
+#define TLP_PROGRAM_EN         BIT(7)
+#define THS_PRE_PROGRAM_EN     BIT(7)
+#define THS_ZERO_PROGRAM_EN    BIT(6)
+
+#define PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL               0x10
+#define PLL_CP_CONTROL_PLL_LOCK_BYPASS                 0x11
+#define PLL_LPF_AND_CP_CONTROL                         0x12
+#define PLL_INPUT_DIVIDER_RATIO                                0x17
+#define PLL_LOOP_DIVIDER_RATIO                         0x18
+#define PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL      0x19
+#define BANDGAP_AND_BIAS_CONTROL                       0x20
+#define TERMINATION_RESISTER_CONTROL                   0x21
+#define AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY                0x22
+#define HS_RX_CONTROL_OF_LANE_CLK                      0x34
+#define HS_RX_CONTROL_OF_LANE_0                                0x44
+#define HS_RX_CONTROL_OF_LANE_1                                0x54
+#define HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL    0x60
+#define HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL    0x61
+#define HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL    0x62
+#define HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL      0x63
+#define HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL       0x64
+#define HS_TX_CLOCK_LANE_POST_TIME_CONTROL             0x65
+#define HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL     0x70
+#define HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL     0x71
+#define HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL     0x72
+#define HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL       0x73
+#define HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL                0x74
+#define HS_RX_DATA_LANE_THS_SETTLE_CONTROL             0x75
+#define HS_RX_CONTROL_OF_LANE_2                                0x84
+#define HS_RX_CONTROL_OF_LANE_3                                0x94
+
+#define DW_MIPI_NEEDS_PHY_CFG_CLK      BIT(0)
+#define DW_MIPI_NEEDS_GRF_CLK          BIT(1)
+
+#define PX30_GRF_PD_VO_CON1            0x0438
+#define PX30_DSI_FORCETXSTOPMODE       (0xf << 7)
+#define PX30_DSI_FORCERXMODE           BIT(6)
+#define PX30_DSI_TURNDISABLE           BIT(5)
+#define PX30_DSI_LCDC_SEL              BIT(0)
+
+#define RK3128_GRF_LVDS_CON0           0x0150
+#define RK3128_DSI_FORCETXSTOPMODE     GENMASK(13, 10)
+#define RK3128_DSI_FORCERXMODE         BIT(9)
+#define RK3128_DSI_TURNDISABLE         BIT(8)
+
+#define RK3288_GRF_SOC_CON6            0x025c
+#define RK3288_DSI0_LCDC_SEL           BIT(6)
+#define RK3288_DSI1_LCDC_SEL           BIT(9)
+
+#define RK3399_GRF_SOC_CON20           0x6250
+#define RK3399_DSI0_LCDC_SEL           BIT(0)
+#define RK3399_DSI1_LCDC_SEL           BIT(4)
+
+#define RK3399_GRF_SOC_CON22           0x6258
+#define RK3399_DSI0_TURNREQUEST                (0xf << 12)
+#define RK3399_DSI0_TURNDISABLE                (0xf << 8)
+#define RK3399_DSI0_FORCETXSTOPMODE    (0xf << 4)
+#define RK3399_DSI0_FORCERXMODE                (0xf << 0)
+
+#define RK3399_GRF_SOC_CON23           0x625c
+#define RK3399_DSI1_TURNDISABLE                (0xf << 12)
+#define RK3399_DSI1_FORCETXSTOPMODE    (0xf << 8)
+#define RK3399_DSI1_FORCERXMODE                (0xf << 4)
+#define RK3399_DSI1_ENABLE             (0xf << 0)
+
+#define RK3399_GRF_SOC_CON24           0x6260
+#define RK3399_TXRX_MASTERSLAVEZ       BIT(7)
+#define RK3399_TXRX_ENABLECLK          BIT(6)
+#define RK3399_TXRX_BASEDIR            BIT(5)
+#define RK3399_TXRX_SRC_SEL_ISP0       BIT(4)
+#define RK3399_TXRX_TURNREQUEST                GENMASK(3, 0)
+
+#define RK3568_GRF_VO_CON2             0x0368
+#define RK3568_DSI0_SKEWCALHS          (0x1f << 11)
+#define RK3568_DSI0_FORCETXSTOPMODE    (0xf << 4)
+#define RK3568_DSI0_TURNDISABLE                BIT(2)
+#define RK3568_DSI0_FORCERXMODE                BIT(0)
+
+/*
+ * Note these registers do not appear in the datasheet, they are
+ * however present in the BSP driver which is where these values
+ * come from. Name GRF_VO_CON3 is assumed.
+ */
+#define RK3568_GRF_VO_CON3             0x36c
+#define RK3568_DSI1_SKEWCALHS          (0x1f << 11)
+#define RK3568_DSI1_FORCETXSTOPMODE    (0xf << 4)
+#define RK3568_DSI1_TURNDISABLE                BIT(2)
+#define RK3568_DSI1_FORCERXMODE                BIT(0)
+
+#define RV1126_GRF_DSIPHY_CON          0x10220
+#define RV1126_DSI_FORCETXSTOPMODE     (0xf << 4)
+#define RV1126_DSI_TURNDISABLE         BIT(2)
+#define RV1126_DSI_FORCERXMODE         BIT(0)
+
+#define HIWORD_UPDATE(val, mask)       (val | (mask) << 16)
+
+enum {
+       DW_DSI_USAGE_IDLE,
+       DW_DSI_USAGE_DSI,
+       DW_DSI_USAGE_PHY,
+};
+
+enum {
+       BANDGAP_97_07,
+       BANDGAP_98_05,
+       BANDGAP_99_02,
+       BANDGAP_100_00,
+       BANDGAP_93_17,
+       BANDGAP_94_15,
+       BANDGAP_95_12,
+       BANDGAP_96_10,
+};
+
+enum {
+       BIASEXTR_87_1,
+       BIASEXTR_91_5,
+       BIASEXTR_95_9,
+       BIASEXTR_100,
+       BIASEXTR_105_94,
+       BIASEXTR_111_88,
+       BIASEXTR_118_8,
+       BIASEXTR_127_7,
+};
+
+struct rockchip_dw_dsi_chip_data {
+       u32 reg;
+
+       u32 lcdsel_grf_reg;
+       u32 lcdsel_big;
+       u32 lcdsel_lit;
+
+       u32 enable_grf_reg;
+       u32 enable;
+
+       u32 lanecfg1_grf_reg;
+       u32 lanecfg1;
+       u32 lanecfg2_grf_reg;
+       u32 lanecfg2;
+
+       int (*dphy_rx_init)(struct phy *phy);
+       int (*dphy_rx_power_on)(struct phy *phy);
+       int (*dphy_rx_power_off)(struct phy *phy);
+
+       unsigned int flags;
+       unsigned int max_data_lanes;
+};
+
+struct dw_mipi_dsi_rockchip {
+       struct device *dev;
+       void __iomem *base;
+
+       struct regmap *grf_regmap;
+       struct clk *pclk;
+       struct clk *pllref_clk;
+       struct clk *grf_clk;
+       struct clk *phy_cfg_clk;
+
+       /* optional external dphy */
+       struct phy *phy;
+       union phy_configure_opts phy_opts;
+
+       /* being a phy for other mipi hosts */
+       unsigned int usage_mode;
+       struct phy *dphy;
+       struct phy_configure_opts_mipi_dphy dphy_config;
+
+       unsigned int lane_mbps; /* per lane */
+       u16 input_div;
+       u16 feedback_div;
+       u32 format;
+
+       struct dw_mipi_dsi *dmd;
+       const struct rockchip_dw_dsi_chip_data *cdata;
+       struct dw_mipi_dsi_plat_data pdata;
+
+       bool dsi_bound;
+};
+
+struct dphy_pll_parameter_map {
+       unsigned int max_mbps;
+       u8 hsfreqrange;
+       u8 icpctrl;
+       u8 lpfctrl;
+};
+
+/* The table is based on 27MHz DPHY pll reference clock. */
+static const struct dphy_pll_parameter_map dppa_map[] = {
+       {  89, 0x00, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM },
+       {  99, 0x10, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM },
+       { 109, 0x20, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM },
+       { 129, 0x01, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+       { 139, 0x11, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+       { 149, 0x21, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+       { 169, 0x02, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM },
+       { 179, 0x12, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM },
+       { 199, 0x22, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM },
+       { 219, 0x03, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM },
+       { 239, 0x13, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM },
+       { 249, 0x23, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM },
+       { 269, 0x04, CP_CURRENT_6UA, LPF_RESISTORS_11_5KOHM },
+       { 299, 0x14, CP_CURRENT_6UA, LPF_RESISTORS_11_5KOHM },
+       { 329, 0x05, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+       { 359, 0x15, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+       { 399, 0x25, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+       { 449, 0x06, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+       { 499, 0x16, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+       { 549, 0x07, CP_CURRENT_7_5UA, LPF_RESISTORS_10_5KOHM },
+       { 599, 0x17, CP_CURRENT_7_5UA, LPF_RESISTORS_10_5KOHM },
+       { 649, 0x08, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+       { 699, 0x18, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+       { 749, 0x09, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+       { 799, 0x19, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+       { 849, 0x29, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+       { 899, 0x39, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+       { 949, 0x0a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
+       { 999, 0x1a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
+       {1049, 0x2a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
+       {1099, 0x3a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
+       {1149, 0x0b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+       {1199, 0x1b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+       {1249, 0x2b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+       {1299, 0x3b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+       {1349, 0x0c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+       {1399, 0x1c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+       {1449, 0x2c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+       {1500, 0x3c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM }
+};
+
+static int max_mbps_to_parameter(unsigned int max_mbps)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dppa_map); i++)
+               if (dppa_map[i].max_mbps >= max_mbps)
+                       return i;
+
+       return -EINVAL;
+}
+
+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
+ */
+static inline unsigned int ns2bc(struct dw_mipi_dsi_rockchip *dsi, int ns)
+{
+       return DIV_ROUND_UP(ns * dsi->lane_mbps / 8, 1000);
+}
+
+/*
+ * ns2ui - Nanoseconds to UI time periods
+ */
+static inline unsigned int ns2ui(struct dw_mipi_dsi_rockchip *dsi, int ns)
+{
+       return DIV_ROUND_UP(ns * dsi->lane_mbps, 1000);
+}
+
+static int dw_mipi_dsi_phy_init(void *priv_data)
+{
+       struct dw_mipi_dsi_rockchip *dsi = priv_data;
+       int ret, i, vco;
+
+       if (dsi->phy)
+               return 0;
+
+       /*
+        * Get vco from frequency(lane_mbps)
+        * vco  frequency table
+        * 000 - between   80 and  200 MHz
+        * 001 - between  200 and  300 MHz
+        * 010 - between  300 and  500 MHz
+        * 011 - between  500 and  700 MHz
+        * 100 - between  700 and  900 MHz
+        * 101 - between  900 and 1100 MHz
+        * 110 - between 1100 and 1300 MHz
+        * 111 - between 1300 and 1500 MHz
+        */
+       vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200;
+
+       i = max_mbps_to_parameter(dsi->lane_mbps);
+       if (i < 0) {
+               DRM_DEV_ERROR(dsi->dev,
+                             "failed to get parameter for %dmbps clock\n",
+                             dsi->lane_mbps);
+               return i;
+       }
+
+       ret = clk_prepare_enable(dsi->phy_cfg_clk);
+       if (ret) {
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n");
+               return ret;
+       }
+
+       dw_mipi_dsi_phy_write(dsi, 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,
+                             CP_CURRENT_SEL(dppa_map[i].icpctrl));
+       dw_mipi_dsi_phy_write(dsi, 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,
+                             HSFREQRANGE_SEL(dppa_map[i].hsfreqrange));
+
+       dw_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO,
+                             INPUT_DIVIDER(dsi->input_div));
+       dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
+                             LOOP_DIV_LOW_SEL(dsi->feedback_div) |
+                             LOW_PROGRAM_EN);
+       /*
+        * We need set PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL immediately
+        * to make the configured LSB effective according to IP simulation
+        * 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,
+                             PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
+       dw_mipi_dsi_phy_write(dsi, 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,
+                             PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
+
+       dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
+                             LOW_PROGRAM_EN | BIASEXTR_SEL(BIASEXTR_127_7));
+       dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
+                             HIGH_PROGRAM_EN | BANDGAP_SEL(BANDGAP_96_10));
+
+       dw_mipi_dsi_phy_write(dsi, BANDGAP_AND_BIAS_CONTROL,
+                             POWER_CONTROL | INTERNAL_REG_CURRENT |
+                             BIAS_BLOCK_ON | BANDGAP_ON);
+
+       dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL,
+                             TER_RESISTOR_LOW | TER_CAL_DONE |
+                             SETRD_MAX | TER_RESISTORS_ON);
+       dw_mipi_dsi_phy_write(dsi, 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,
+                             TLP_PROGRAM_EN | ns2bc(dsi, 500));
+       dw_mipi_dsi_phy_write(dsi, 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,
+                             THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300));
+       dw_mipi_dsi_phy_write(dsi, 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,
+                             BIT(5) | ns2bc(dsi, 100));
+       dw_mipi_dsi_phy_write(dsi, 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,
+                             TLP_PROGRAM_EN | ns2bc(dsi, 500));
+       dw_mipi_dsi_phy_write(dsi, 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,
+                             THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2));
+       dw_mipi_dsi_phy_write(dsi, 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,
+                             BIT(5) | ns2bc(dsi, 100));
+
+       clk_disable_unprepare(dsi->phy_cfg_clk);
+
+       return ret;
+}
+
+static void dw_mipi_dsi_phy_power_on(void *priv_data)
+{
+       struct dw_mipi_dsi_rockchip *dsi = priv_data;
+       int ret;
+
+       ret = phy_set_mode(dsi->phy, PHY_MODE_MIPI_DPHY);
+       if (ret) {
+               DRM_DEV_ERROR(dsi->dev, "failed to set phy mode: %d\n", ret);
+               return;
+       }
+
+       phy_configure(dsi->phy, &dsi->phy_opts);
+       phy_power_on(dsi->phy);
+}
+
+static void dw_mipi_dsi_phy_power_off(void *priv_data)
+{
+       struct dw_mipi_dsi_rockchip *dsi = priv_data;
+
+       phy_power_off(dsi->phy);
+}
+
+static int
+dw_mipi_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 dw_mipi_dsi_rockchip *dsi = priv_data;
+       int bpp;
+       unsigned long mpclk, tmp;
+       unsigned int target_mbps = 1000;
+       unsigned int max_mbps = dppa_map[ARRAY_SIZE(dppa_map) - 1].max_mbps;
+       unsigned long best_freq = 0;
+       unsigned long fvco_min, fvco_max, fin, fout;
+       unsigned int min_prediv, max_prediv;
+       unsigned int _prediv, best_prediv;
+       unsigned long _fbdiv, best_fbdiv;
+       unsigned long min_delta = ULONG_MAX;
+
+       dsi->format = format;
+       bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+       if (bpp < 0) {
+               DRM_DEV_ERROR(dsi->dev,
+                             "failed to get bpp for pixel format %d\n",
+                             dsi->format);
+               return bpp;
+       }
+
+       mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC);
+       if (mpclk) {
+               /* take 1 / 0.8, since mbps must big than bandwidth of RGB */
+               tmp = mpclk * (bpp / lanes) * 10 / 8;
+               if (tmp < max_mbps)
+                       target_mbps = tmp;
+               else
+                       DRM_DEV_ERROR(dsi->dev,
+                                     "DPHY clock frequency is out of range\n");
+       }
+
+       /* for external phy only a the mipi_dphy_config is necessary */
+       if (dsi->phy) {
+               phy_mipi_dphy_get_default_config(mode->clock * 1000 * 10 / 8,
+                                                bpp, lanes,
+                                                &dsi->phy_opts.mipi_dphy);
+               dsi->lane_mbps = target_mbps;
+               *lane_mbps = dsi->lane_mbps;
+
+               return 0;
+       }
+
+       fin = clk_get_rate(dsi->pllref_clk);
+       fout = target_mbps * USEC_PER_SEC;
+
+       /* constraint: 5Mhz <= Fref / N <= 40MHz */
+       min_prediv = DIV_ROUND_UP(fin, 40 * USEC_PER_SEC);
+       max_prediv = fin / (5 * USEC_PER_SEC);
+
+       /* constraint: 80MHz <= Fvco <= 1500Mhz */
+       fvco_min = 80 * USEC_PER_SEC;
+       fvco_max = 1500 * USEC_PER_SEC;
+
+       for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
+               u64 tmp;
+               u32 delta;
+               /* Fvco = Fref * M / N */
+               tmp = (u64)fout * _prediv;
+               do_div(tmp, fin);
+               _fbdiv = tmp;
+               /*
+                * Due to the use of a "by 2 pre-scaler," the range of the
+                * feedback multiplication value M is limited to even division
+                * numbers, and m must be greater than 6, not bigger than 512.
+                */
+               if (_fbdiv < 6 || _fbdiv > 512)
+                       continue;
+
+               _fbdiv += _fbdiv % 2;
+
+               tmp = (u64)_fbdiv * fin;
+               do_div(tmp, _prediv);
+               if (tmp < fvco_min || tmp > fvco_max)
+                       continue;
+
+               delta = abs(fout - tmp);
+               if (delta < min_delta) {
+                       best_prediv = _prediv;
+                       best_fbdiv = _fbdiv;
+                       min_delta = delta;
+                       best_freq = tmp;
+               }
+       }
+
+       if (best_freq) {
+               dsi->lane_mbps = DIV_ROUND_UP(best_freq, USEC_PER_SEC);
+               *lane_mbps = dsi->lane_mbps;
+               dsi->input_div = best_prediv;
+               dsi->feedback_div = best_fbdiv;
+       } else {
+               DRM_DEV_ERROR(dsi->dev, "Can not find best_freq for DPHY\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct hstt {
+       unsigned int maxfreq;
+       struct dw_mipi_dsi_dphy_timing timing;
+};
+
+#define HSTT(_maxfreq, _c_lp2hs, _c_hs2lp, _d_lp2hs, _d_hs2lp) \
+{                                      \
+       .maxfreq = _maxfreq,            \
+       .timing = {                     \
+               .clk_lp2hs = _c_lp2hs,  \
+               .clk_hs2lp = _c_hs2lp,  \
+               .data_lp2hs = _d_lp2hs, \
+               .data_hs2lp = _d_hs2lp, \
+       }                               \
+}
+
+/* Table A-3 High-Speed Transition Times */
+static struct hstt hstt_table[] = {
+       HSTT(  90,  32, 20,  26, 13),
+       HSTT( 100,  35, 23,  28, 14),
+       HSTT( 110,  32, 22,  26, 13),
+       HSTT( 130,  31, 20,  27, 13),
+       HSTT( 140,  33, 22,  26, 14),
+       HSTT( 150,  33, 21,  26, 14),
+       HSTT( 170,  32, 20,  27, 13),
+       HSTT( 180,  36, 23,  30, 15),
+       HSTT( 200,  40, 22,  33, 15),
+       HSTT( 220,  40, 22,  33, 15),
+       HSTT( 240,  44, 24,  36, 16),
+       HSTT( 250,  48, 24,  38, 17),
+       HSTT( 270,  48, 24,  38, 17),
+       HSTT( 300,  50, 27,  41, 18),
+       HSTT( 330,  56, 28,  45, 18),
+       HSTT( 360,  59, 28,  48, 19),
+       HSTT( 400,  61, 30,  50, 20),
+       HSTT( 450,  67, 31,  55, 21),
+       HSTT( 500,  73, 31,  59, 22),
+       HSTT( 550,  79, 36,  63, 24),
+       HSTT( 600,  83, 37,  68, 25),
+       HSTT( 650,  90, 38,  73, 27),
+       HSTT( 700,  95, 40,  77, 28),
+       HSTT( 750, 102, 40,  84, 28),
+       HSTT( 800, 106, 42,  87, 30),
+       HSTT( 850, 113, 44,  93, 31),
+       HSTT( 900, 118, 47,  98, 32),
+       HSTT( 950, 124, 47, 102, 34),
+       HSTT(1000, 130, 49, 107, 35),
+       HSTT(1050, 135, 51, 111, 37),
+       HSTT(1100, 139, 51, 114, 38),
+       HSTT(1150, 146, 54, 120, 40),
+       HSTT(1200, 153, 57, 125, 41),
+       HSTT(1250, 158, 58, 130, 42),
+       HSTT(1300, 163, 58, 135, 44),
+       HSTT(1350, 168, 60, 140, 45),
+       HSTT(1400, 172, 64, 144, 47),
+       HSTT(1450, 176, 65, 148, 48),
+       HSTT(1500, 181, 66, 153, 50)
+};
+
+static int
+dw_mipi_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].maxfreq)
+                       break;
+
+       if (i == ARRAY_SIZE(hstt_table))
+               i--;
+
+       *timing = hstt_table[i].timing;
+
+       return 0;
+}
+
+static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_rockchip_phy_ops = {
+       .init = dw_mipi_dsi_phy_init,
+       .power_on = dw_mipi_dsi_phy_power_on,
+       .power_off = dw_mipi_dsi_phy_power_off,
+       .get_lane_mbps = dw_mipi_dsi_get_lane_mbps,
+       .get_timing = dw_mipi_dsi_phy_get_timing,
+};
+
+static void dw_mipi_dsi_rockchip_config(struct dw_mipi_dsi_rockchip *dsi)
+{
+       if (dsi->cdata->lanecfg1_grf_reg)
+               regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg1_grf_reg,
+                                             dsi->cdata->lanecfg1);
+
+       if (dsi->cdata->lanecfg2_grf_reg)
+               regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg2_grf_reg,
+                                             dsi->cdata->lanecfg2);
+
+       if (dsi->cdata->enable_grf_reg)
+               regmap_write(dsi->grf_regmap, dsi->cdata->enable_grf_reg,
+                                             dsi->cdata->enable);
+}
+
+static void dw_mipi_dsi_rockchip_set_lcdsel(struct dw_mipi_dsi_rockchip *dsi,
+                                           int mux)
+{
+       if (!dsi->cdata->lcdsel_grf_reg)
+               return;
+
+       /* See comment inside dw_mipi_dsi_encoder_enable() for details */
+       pr_warn("lcdsel hardcoded to vopb\n");
+
+       regmap_write(dsi->grf_regmap, dsi->cdata->lcdsel_grf_reg,
+               mux ? dsi->cdata->lcdsel_lit : dsi->cdata->lcdsel_big);
+}
+
+static void dw_mipi_dsi_encoder_enable(struct dw_mipi_dsi_rockchip *dsi)
+{
+       int ret, mux;
+
+       /*
+        * TODO: We do not resolve the video pipeline ahead of time, so we
+        * currently have no easy way to impement Linux'
+        * drm_of_encoder_active_endpoint_id() in barebox.
+        *
+        * It's only needed for MIPI-DSI hosts with vopb/vopl ports though,
+        * not for the RK356x, so let's harcode the 0 and print a warning
+        * should we get e.g. RK3399 VOP1 support in future.
+        */
+       mux = 0;
+
+       /*
+        * For the RK3399, the clk of grf must be enabled before writing grf
+        * register. And for RK3288 or other soc, this grf_clk must be NULL,
+        * the clk_prepare_enable return true directly.
+        */
+       ret = clk_prepare_enable(dsi->grf_clk);
+       if (ret) {
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
+               return;
+       }
+
+       dw_mipi_dsi_rockchip_set_lcdsel(dsi, mux);
+
+       clk_disable_unprepare(dsi->grf_clk);
+}
+
+static int dw_mipi_dsi_rockchip_bind(struct dw_mipi_dsi_rockchip *dsi)
+{
+       struct device *dev = dsi->dev;
+       int ret;
+
+       ret = clk_prepare_enable(dsi->pllref_clk);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "Failed to enable pllref_clk: %d\n", ret);
+               goto out_pm_runtime;
+       }
+
+       /*
+        * With the GRF clock running, write lane and dual-mode configurations
+        * that won't change immediately. If we waited until enable() to do
+        * this, things like panel preparation would not be able to send
+        * commands over DSI.
+        */
+       ret = clk_prepare_enable(dsi->grf_clk);
+       if (ret) {
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
+               goto out_pll_clk;
+       }
+
+       dw_mipi_dsi_rockchip_config(dsi);
+
+       clk_disable_unprepare(dsi->grf_clk);
+
+       dw_mipi_dsi_encoder_enable(dsi);
+
+       dsi->dsi_bound = true;
+
+       return 0;
+
+out_pll_clk:
+       clk_disable_unprepare(dsi->pllref_clk);
+out_pm_runtime:
+
+       return ret;
+}
+
+static int dw_mipi_dsi_rockchip_host_attach(void *priv_data,
+                                           struct mipi_dsi_device *device)
+{
+       struct dw_mipi_dsi_rockchip *dsi = priv_data;
+       int ret;
+
+       if (dsi->usage_mode != DW_DSI_USAGE_IDLE) {
+               DRM_DEV_ERROR(dsi->dev, "dsi controller already in use\n");
+               return -EBUSY;
+       }
+
+       dsi->usage_mode = DW_DSI_USAGE_DSI;
+
+       ret = dw_mipi_dsi_rockchip_bind(dsi);
+       if (ret) {
+               DRM_DEV_ERROR(dsi->dev, "Failed to register component: %d\n",
+                                       ret);
+               goto out;
+       }
+
+       return 0;
+
+out:
+       dsi->usage_mode = DW_DSI_USAGE_IDLE;
+       return ret;
+}
+
+static const struct dw_mipi_dsi_host_ops dw_mipi_dsi_rockchip_host_ops = {
+       .attach = dw_mipi_dsi_rockchip_host_attach,
+};
+
+static int dw_mipi_dsi_dphy_init(struct phy *phy)
+{
+       struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy);
+       int ret;
+
+       if (dsi->usage_mode != DW_DSI_USAGE_IDLE) {
+               DRM_DEV_ERROR(dsi->dev, "dsi controller already in use\n");
+               return -EBUSY;
+       }
+
+       dsi->usage_mode = DW_DSI_USAGE_PHY;
+
+       if (dsi->cdata->dphy_rx_init) {
+               ret = clk_prepare_enable(dsi->pclk);
+               if (ret < 0)
+                       goto err_init;
+
+               ret = clk_prepare_enable(dsi->grf_clk);
+               if (ret) {
+                       clk_disable_unprepare(dsi->pclk);
+                       goto err_init;
+               }
+
+               ret = dsi->cdata->dphy_rx_init(phy);
+               clk_disable_unprepare(dsi->grf_clk);
+               clk_disable_unprepare(dsi->pclk);
+               if (ret < 0)
+                       goto err_init;
+       }
+
+       return 0;
+
+err_init:
+       dsi->usage_mode = DW_DSI_USAGE_IDLE;
+
+       return ret;
+}
+
+static int dw_mipi_dsi_dphy_exit(struct phy *phy)
+{
+       struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy);
+
+       dsi->usage_mode = DW_DSI_USAGE_IDLE;
+
+       return 0;
+}
+
+static int dw_mipi_dsi_dphy_configure(struct phy *phy, union 
phy_configure_opts *opts)
+{
+       struct phy_configure_opts_mipi_dphy *config = &opts->mipi_dphy;
+       struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy);
+       int ret;
+
+       ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
+       if (ret)
+               return ret;
+
+       dsi->dphy_config = *config;
+       dsi->lane_mbps = div_u64(config->hs_clk_rate, 1000 * 1000 * 1);
+
+       return 0;
+}
+
+static int dw_mipi_dsi_dphy_power_on(struct phy *phy)
+{
+       struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy);
+       int i, ret;
+
+       DRM_DEV_DEBUG(dsi->dev, "lanes %d - data_rate_mbps %u\n",
+                     dsi->dphy_config.lanes, dsi->lane_mbps);
+
+       i = max_mbps_to_parameter(dsi->lane_mbps);
+       if (i < 0) {
+               DRM_DEV_ERROR(dsi->dev, "failed to get parameter for %dmbps 
clock\n",
+                             dsi->lane_mbps);
+               return i;
+       }
+
+       ret = clk_prepare_enable(dsi->pclk);
+       if (ret) {
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk: %d\n", ret);
+               goto err_pclk;
+       }
+
+       ret = clk_prepare_enable(dsi->grf_clk);
+       if (ret) {
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
+               goto err_grf_clk;
+       }
+
+       ret = clk_prepare_enable(dsi->phy_cfg_clk);
+       if (ret) {
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk: %d\n", 
ret);
+               goto err_phy_cfg_clk;
+       }
+
+       /* do soc-variant specific init */
+       if (dsi->cdata->dphy_rx_power_on) {
+               ret = dsi->cdata->dphy_rx_power_on(phy);
+               if (ret < 0) {
+                       DRM_DEV_ERROR(dsi->dev, "hardware-specific phy bringup 
failed: %d\n", ret);
+                       goto err_pwr_on;
+               }
+       }
+
+       /*
+        * 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,
+                             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);
+
+       /* Normal operation */
+       dw_mipi_dsi_phy_write(dsi, 0x0, 0);
+
+       clk_disable_unprepare(dsi->phy_cfg_clk);
+       clk_disable_unprepare(dsi->grf_clk);
+
+       return ret;
+
+err_pwr_on:
+       clk_disable_unprepare(dsi->phy_cfg_clk);
+err_phy_cfg_clk:
+       clk_disable_unprepare(dsi->grf_clk);
+err_grf_clk:
+       clk_disable_unprepare(dsi->pclk);
+err_pclk:
+       return ret;
+}
+
+static int dw_mipi_dsi_dphy_power_off(struct phy *phy)
+{
+       struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy);
+       int ret;
+
+       ret = clk_prepare_enable(dsi->grf_clk);
+       if (ret) {
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
+               return ret;
+       }
+
+       if (dsi->cdata->dphy_rx_power_off) {
+               ret = dsi->cdata->dphy_rx_power_off(phy);
+               if (ret < 0)
+                       DRM_DEV_ERROR(dsi->dev, "hardware-specific phy shutdown 
failed: %d\n", ret);
+       }
+
+       clk_disable_unprepare(dsi->grf_clk);
+       clk_disable_unprepare(dsi->pclk);
+
+       return ret;
+}
+
+static const struct phy_ops dw_mipi_dsi_dphy_ops = {
+       .configure      = dw_mipi_dsi_dphy_configure,
+       .power_on       = dw_mipi_dsi_dphy_power_on,
+       .power_off      = dw_mipi_dsi_dphy_power_off,
+       .init           = dw_mipi_dsi_dphy_init,
+       .exit           = dw_mipi_dsi_dphy_exit,
+};
+
+static int dw_mipi_dsi_rockchip_probe(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct dw_mipi_dsi_rockchip *dsi;
+       struct phy_provider *phy_provider;
+       struct resource *res;
+       const struct rockchip_dw_dsi_chip_data *cdata =
+                               of_device_get_match_data(dev);
+       int ret, i;
+
+       dsi = calloc(sizeof(*dsi), 1);
+       if (!dsi)
+               return -ENOMEM;
+
+       dsi->base = dev_platform_get_and_ioremap_resource(dev, 0, &res);
+       if (IS_ERR(dsi->base)) {
+               DRM_DEV_ERROR(dev, "Unable to get dsi registers\n");
+               return PTR_ERR(dsi->base);
+       }
+
+       i = 0;
+       while (cdata[i].reg) {
+               if (cdata[i].reg == res->start) {
+                       dsi->cdata = &cdata[i];
+                       break;
+               }
+
+               i++;
+       }
+
+       if (!dsi->cdata) {
+               DRM_DEV_ERROR(dev, "no dsi-config for %s node\n", np->name);
+               return -EINVAL;
+       }
+
+       /* try to get a possible external dphy */
+       dsi->phy = phy_optional_get(dev, "dphy");
+       if (IS_ERR(dsi->phy)) {
+               ret = PTR_ERR(dsi->phy);
+               DRM_DEV_ERROR(dev, "failed to get mipi dphy: %d\n", ret);
+               return ret;
+       }
+
+       dsi->pclk = clk_get(dev, "pclk");
+       if (IS_ERR(dsi->pclk)) {
+               ret = PTR_ERR(dsi->pclk);
+               DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret);
+               return ret;
+       }
+
+       dsi->pllref_clk = clk_get(dev, "ref");
+       if (IS_ERR(dsi->pllref_clk)) {
+               if (dsi->phy) {
+                       /*
+                        * if external phy is present, pll will be
+                        * generated there.
+                        */
+                       dsi->pllref_clk = NULL;
+               } else {
+                       ret = PTR_ERR(dsi->pllref_clk);
+                       DRM_DEV_ERROR(dev,
+                                     "Unable to get pll reference clock: %d\n",
+                                     ret);
+                       return ret;
+               }
+       }
+
+       if (dsi->cdata->flags & DW_MIPI_NEEDS_PHY_CFG_CLK) {
+               dsi->phy_cfg_clk = clk_get(dev, "phy_cfg");
+               if (IS_ERR(dsi->phy_cfg_clk)) {
+                       ret = PTR_ERR(dsi->phy_cfg_clk);
+                       DRM_DEV_ERROR(dev,
+                                     "Unable to get phy_cfg_clk: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       if (dsi->cdata->flags & DW_MIPI_NEEDS_GRF_CLK) {
+               dsi->grf_clk = clk_get(dev, "grf");
+               if (IS_ERR(dsi->grf_clk)) {
+                       ret = PTR_ERR(dsi->grf_clk);
+                       DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+       if (IS_ERR(dsi->grf_regmap)) {
+               DRM_DEV_ERROR(dev, "Unable to get rockchip,grf\n");
+               return PTR_ERR(dsi->grf_regmap);
+       }
+
+       dsi->dev = dev;
+       dsi->pdata.base = dsi->base;
+       dsi->pdata.max_data_lanes = dsi->cdata->max_data_lanes;
+       dsi->pdata.phy_ops = &dw_mipi_dsi_rockchip_phy_ops;
+       dsi->pdata.host_ops = &dw_mipi_dsi_rockchip_host_ops;
+       dsi->pdata.priv_data = dsi;
+       dev_set_drvdata(dev, dsi);
+
+       dsi->dphy = phy_create(dev, NULL, &dw_mipi_dsi_dphy_ops);
+       if (IS_ERR(dsi->dphy)) {
+               DRM_DEV_ERROR(dev, "failed to create PHY\n");
+               return PTR_ERR(dsi->dphy);
+       }
+
+       phy_set_drvdata(dsi->dphy, dsi);
+       phy_provider = of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       ret = clk_prepare_enable(dsi->pllref_clk);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "Failed to enable pllref_clk: %d\n", ret);
+               return ret;
+       }
+
+       dw_mipi_dsi_rockchip_config(dsi);
+
+       dsi->dmd = dw_mipi_dsi_bind(dev, &dsi->pdata);
+       if (IS_ERR(dsi->dmd)) {
+               dev_err(dev, "failed to bind: %pe\n", dsi->dmd);
+               return PTR_ERR(dsi->dmd);
+       }
+
+       return 0;
+}
+
+static const struct rockchip_dw_dsi_chip_data px30_chip_data[] = {
+       {
+               .reg = 0xff450000,
+               .lcdsel_grf_reg = PX30_GRF_PD_VO_CON1,
+               .lcdsel_big = HIWORD_UPDATE(0, PX30_DSI_LCDC_SEL),
+               .lcdsel_lit = HIWORD_UPDATE(PX30_DSI_LCDC_SEL,
+                                           PX30_DSI_LCDC_SEL),
+
+               .lanecfg1_grf_reg = PX30_GRF_PD_VO_CON1,
+               .lanecfg1 = HIWORD_UPDATE(0, PX30_DSI_TURNDISABLE |
+                                            PX30_DSI_FORCERXMODE |
+                                            PX30_DSI_FORCETXSTOPMODE),
+
+               .max_data_lanes = 4,
+       },
+       { /* sentinel */ }
+};
+
+static const struct rockchip_dw_dsi_chip_data rk3128_chip_data[] = {
+       {
+               .reg = 0x10110000,
+               .lanecfg1_grf_reg = RK3128_GRF_LVDS_CON0,
+               .lanecfg1 = HIWORD_UPDATE(0, RK3128_DSI_TURNDISABLE |
+                                            RK3128_DSI_FORCERXMODE |
+                                            RK3128_DSI_FORCETXSTOPMODE),
+               .max_data_lanes = 4,
+       },
+       { /* sentinel */ }
+};
+
+static const struct rockchip_dw_dsi_chip_data rk3288_chip_data[] = {
+       {
+               .reg = 0xff960000,
+               .lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
+               .lcdsel_big = HIWORD_UPDATE(0, RK3288_DSI0_LCDC_SEL),
+               .lcdsel_lit = HIWORD_UPDATE(RK3288_DSI0_LCDC_SEL, 
RK3288_DSI0_LCDC_SEL),
+
+               .max_data_lanes = 4,
+       },
+       {
+               .reg = 0xff964000,
+               .lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
+               .lcdsel_big = HIWORD_UPDATE(0, RK3288_DSI1_LCDC_SEL),
+               .lcdsel_lit = HIWORD_UPDATE(RK3288_DSI1_LCDC_SEL, 
RK3288_DSI1_LCDC_SEL),
+
+               .max_data_lanes = 4,
+       },
+       { /* sentinel */ }
+};
+
+static int rk3399_dphy_tx1rx1_init(struct phy *phy)
+{
+       struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy);
+
+       /*
+        * Set TX1RX1 source to isp1.
+        * Assume ISP0 is supplied by the RX0 dphy.
+        */
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON24,
+                    HIWORD_UPDATE(0, RK3399_TXRX_SRC_SEL_ISP0));
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON24,
+                    HIWORD_UPDATE(0, RK3399_TXRX_MASTERSLAVEZ));
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON24,
+                    HIWORD_UPDATE(0, RK3399_TXRX_BASEDIR));
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON23,
+                    HIWORD_UPDATE(0, RK3399_DSI1_ENABLE));
+
+       return 0;
+}
+
+static int rk3399_dphy_tx1rx1_power_on(struct phy *phy)
+{
+       struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy);
+
+       /* tester reset pulse */
+       dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_TESTCLR);
+       udelay(100);
+
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON24,
+                    HIWORD_UPDATE(0, RK3399_TXRX_MASTERSLAVEZ));
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON24,
+                    HIWORD_UPDATE(RK3399_TXRX_BASEDIR, RK3399_TXRX_BASEDIR));
+
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON23,
+                    HIWORD_UPDATE(0, RK3399_DSI1_FORCERXMODE));
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON23,
+                    HIWORD_UPDATE(0, RK3399_DSI1_FORCETXSTOPMODE));
+
+       /* Disable lane turn around, which is ignored in receive mode */
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON24,
+                    HIWORD_UPDATE(0, RK3399_TXRX_TURNREQUEST));
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON23,
+                    HIWORD_UPDATE(RK3399_DSI1_TURNDISABLE,
+                                  RK3399_DSI1_TURNDISABLE));
+       udelay(100);
+
+       dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+       udelay(100);
+
+       /* Enable dphy lanes */
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON23,
+                    HIWORD_UPDATE(GENMASK(dsi->dphy_config.lanes - 1, 0),
+                                  RK3399_DSI1_ENABLE));
+
+       udelay(100);
+
+       return 0;
+}
+
+static int rk3399_dphy_tx1rx1_power_off(struct phy *phy)
+{
+       struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy);
+
+       regmap_write(dsi->grf_regmap, RK3399_GRF_SOC_CON23,
+                    HIWORD_UPDATE(0, RK3399_DSI1_ENABLE));
+
+       return 0;
+}
+
+static const struct rockchip_dw_dsi_chip_data rk3399_chip_data[] = {
+       {
+               .reg = 0xff960000,
+               .lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
+               .lcdsel_big = HIWORD_UPDATE(0, RK3399_DSI0_LCDC_SEL),
+               .lcdsel_lit = HIWORD_UPDATE(RK3399_DSI0_LCDC_SEL,
+                                           RK3399_DSI0_LCDC_SEL),
+
+               .lanecfg1_grf_reg = RK3399_GRF_SOC_CON22,
+               .lanecfg1 = HIWORD_UPDATE(0, RK3399_DSI0_TURNREQUEST |
+                                            RK3399_DSI0_TURNDISABLE |
+                                            RK3399_DSI0_FORCETXSTOPMODE |
+                                            RK3399_DSI0_FORCERXMODE),
+
+               .flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK,
+               .max_data_lanes = 4,
+       },
+       {
+               .reg = 0xff968000,
+               .lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
+               .lcdsel_big = HIWORD_UPDATE(0, RK3399_DSI1_LCDC_SEL),
+               .lcdsel_lit = HIWORD_UPDATE(RK3399_DSI1_LCDC_SEL,
+                                           RK3399_DSI1_LCDC_SEL),
+
+               .lanecfg1_grf_reg = RK3399_GRF_SOC_CON23,
+               .lanecfg1 = HIWORD_UPDATE(0, RK3399_DSI1_TURNDISABLE |
+                                            RK3399_DSI1_FORCETXSTOPMODE |
+                                            RK3399_DSI1_FORCERXMODE |
+                                            RK3399_DSI1_ENABLE),
+
+               .lanecfg2_grf_reg = RK3399_GRF_SOC_CON24,
+               .lanecfg2 = HIWORD_UPDATE(RK3399_TXRX_MASTERSLAVEZ |
+                                         RK3399_TXRX_ENABLECLK,
+                                         RK3399_TXRX_MASTERSLAVEZ |
+                                         RK3399_TXRX_ENABLECLK |
+                                         RK3399_TXRX_BASEDIR),
+
+               .enable_grf_reg = RK3399_GRF_SOC_CON23,
+               .enable = HIWORD_UPDATE(RK3399_DSI1_ENABLE, RK3399_DSI1_ENABLE),
+
+               .flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK,
+               .max_data_lanes = 4,
+
+               .dphy_rx_init = rk3399_dphy_tx1rx1_init,
+               .dphy_rx_power_on = rk3399_dphy_tx1rx1_power_on,
+               .dphy_rx_power_off = rk3399_dphy_tx1rx1_power_off,
+       },
+       { /* sentinel */ }
+};
+
+static const struct rockchip_dw_dsi_chip_data rk3568_chip_data[] = {
+       {
+               .reg = 0xfe060000,
+               .lanecfg1_grf_reg = RK3568_GRF_VO_CON2,
+               .lanecfg1 = HIWORD_UPDATE(0, RK3568_DSI0_SKEWCALHS |
+                                         RK3568_DSI0_FORCETXSTOPMODE |
+                                         RK3568_DSI0_TURNDISABLE |
+                                         RK3568_DSI0_FORCERXMODE),
+               .max_data_lanes = 4,
+       },
+       {
+               .reg = 0xfe070000,
+               .lanecfg1_grf_reg = RK3568_GRF_VO_CON3,
+               .lanecfg1 = HIWORD_UPDATE(0, RK3568_DSI1_SKEWCALHS |
+                                         RK3568_DSI1_FORCETXSTOPMODE |
+                                         RK3568_DSI1_TURNDISABLE |
+                                         RK3568_DSI1_FORCERXMODE),
+               .max_data_lanes = 4,
+       },
+       { /* sentinel */ }
+};
+
+static const struct rockchip_dw_dsi_chip_data rv1126_chip_data[] = {
+       {
+               .reg = 0xffb30000,
+               .lanecfg1_grf_reg = RV1126_GRF_DSIPHY_CON,
+               .lanecfg1 = HIWORD_UPDATE(0, RV1126_DSI_TURNDISABLE |
+                                            RV1126_DSI_FORCERXMODE |
+                                            RV1126_DSI_FORCETXSTOPMODE),
+               .max_data_lanes = 4,
+       },
+       { /* sentinel */ }
+};
+
+static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = {
+       {
+        .compatible = "rockchip,px30-mipi-dsi",
+        .data = &px30_chip_data,
+       }, {
+        .compatible = "rockchip,rk3128-mipi-dsi",
+        .data = &rk3128_chip_data,
+       }, {
+        .compatible = "rockchip,rk3288-mipi-dsi",
+        .data = &rk3288_chip_data,
+       }, {
+        .compatible = "rockchip,rk3399-mipi-dsi",
+        .data = &rk3399_chip_data,
+       }, {
+        .compatible = "rockchip,rk3568-mipi-dsi",
+        .data = &rk3568_chip_data,
+       }, {
+        .compatible = "rockchip,rv1126-mipi-dsi",
+        .data = &rv1126_chip_data,
+       },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi_rockchip_dt_ids);
+
+struct driver dw_mipi_dsi_rockchip_driver = {
+       .name           = "dw-mipi-dsi-rockchip",
+       .probe          = dw_mipi_dsi_rockchip_probe,
+       .of_match_table = dw_mipi_dsi_rockchip_dt_ids,
+};
+device_platform_driver(dw_mipi_dsi_rockchip_driver);
diff --git a/drivers/video/rockchip/rockchip_drm_vop2.c 
b/drivers/video/rockchip/rockchip_drm_vop2.c
index f42b0c48eede..fd3493c0a802 100644
--- a/drivers/video/rockchip/rockchip_drm_vop2.c
+++ b/drivers/video/rockchip/rockchip_drm_vop2.c
@@ -1420,7 +1420,8 @@ static int vop2_output_mode(u32 bus_format, int 
crtc_endpoint_id)
        if (vop2_output_if_is_hdmi(crtc_endpoint_id))
                return ROCKCHIP_OUT_MODE_AAAA;
 
-       if (vop2_output_if_is_dpi(crtc_endpoint_id))
+       if (vop2_output_if_is_dpi(crtc_endpoint_id) ||
+           vop2_output_if_is_mipi(crtc_endpoint_id))
                switch (bus_format) {
                case MEDIA_BUS_FMT_RGB666_1X18:
                        return ROCKCHIP_OUT_MODE_P666;
-- 
2.47.3


Reply via email to