[coreboot-gerrit] Change in coreboot[master]: rockchip/rk3399: Add MIPI driver

Julius Werner (Code Review) gerrit at coreboot.org
Thu May 18 01:00:14 CEST 2017


Julius Werner has submitted this change and it was merged. ( https://review.coreboot.org/19477 )

Change subject: rockchip/rk3399: Add MIPI driver
......................................................................


rockchip/rk3399: Add MIPI driver

This patch configures clock for mipi and then
adds mipi driver for support innolux-p079zca
mipi panel in rk3399 scarlet.

Change-Id: I02475eefb187c619c614b1cd20e97074bc8d917f
Signed-off-by: Nickey Yang <nickey.yang at rock-chips.com>
Reviewed-on: https://review.coreboot.org/19477
Tested-by: build bot (Jenkins) <no-reply at coreboot.org>
Reviewed-by: Julius Werner <jwerner at chromium.org>
---
M src/soc/rockchip/common/include/soc/vop.h
M src/soc/rockchip/common/vop.c
M src/soc/rockchip/rk3399/Makefile.inc
M src/soc/rockchip/rk3399/chip.h
M src/soc/rockchip/rk3399/clock.c
M src/soc/rockchip/rk3399/display.c
M src/soc/rockchip/rk3399/include/soc/addressmap.h
M src/soc/rockchip/rk3399/include/soc/clock.h
A src/soc/rockchip/rk3399/include/soc/mipi.h
A src/soc/rockchip/rk3399/mipi.c
10 files changed, 800 insertions(+), 16 deletions(-)

Approvals:
  Julius Werner: Looks good to me, approved
  build bot (Jenkins): Verified



diff --git a/src/soc/rockchip/common/include/soc/vop.h b/src/soc/rockchip/common/include/soc/vop.h
index 98ad082..c5c5425 100644
--- a/src/soc/rockchip/common/include/soc/vop.h
+++ b/src/soc/rockchip/common/include/soc/vop.h
@@ -119,6 +119,7 @@
 	 */
 	VOP_MODE_EDP = 0,
 	VOP_MODE_HDMI,
+	VOP_MODE_MIPI,
 	VOP_MODE_NONE,
 	VOP_MODE_AUTO_DETECT,
 	VOP_MODE_UNKNOWN,
diff --git a/src/soc/rockchip/common/vop.c b/src/soc/rockchip/common/vop.c
index 629072e..70d59bd 100644
--- a/src/soc/rockchip/common/vop.c
+++ b/src/soc/rockchip/common/vop.c
@@ -24,7 +24,6 @@
 #include <soc/edp.h>
 #include <soc/vop.h>
 
-
 static struct rockchip_vop_regs * const vop_regs[] = {
 	(struct rockchip_vop_regs *)VOP_BIG_BASE,
 	(struct rockchip_vop_regs *)VOP_LIT_BASE
@@ -109,6 +108,7 @@
 	u32 vfront_porch = edid->mode.vso;
 	u32 vsync_len = edid->mode.vspw;
 	u32 vback_porch = edid->mode.vbl - edid->mode.vso - edid->mode.vspw;
+	u32 dsp_out_mode;
 	struct rockchip_vop_regs *preg = vop_regs[vop_id];
 
 	switch (mode) {
@@ -116,17 +116,25 @@
 	case VOP_MODE_HDMI:
 		clrsetbits_le32(&preg->sys_ctrl,
 				M_ALL_OUT_EN, V_HDMI_OUT_EN(1));
+		dsp_out_mode = 15;
 		break;
-
+	case VOP_MODE_MIPI:
+		clrsetbits_le32(&preg->sys_ctrl,
+				M_ALL_OUT_EN, V_MIPI_OUT_EN(1));
+		dsp_out_mode = 0;
+		break;
 	case VOP_MODE_EDP:
 	default:
 		clrsetbits_le32(&preg->sys_ctrl,
 				M_ALL_OUT_EN, V_EDP_OUT_EN(1));
+		dsp_out_mode = 15;
 		break;
 	}
+
 	clrsetbits_le32(&preg->dsp_ctrl0,
-			M_DSP_OUT_MODE | M_DSP_VSYNC_POL | M_DSP_HSYNC_POL,
-			V_DSP_OUT_MODE(15) |
+			M_DSP_OUT_MODE | M_DSP_VSYNC_POL |
+			M_DSP_HSYNC_POL,
+			V_DSP_OUT_MODE(dsp_out_mode) |
 			V_DSP_HSYNC_POL(edid->mode.phsync == '+') |
 			V_DSP_VSYNC_POL(edid->mode.pvsync == '+'));
 
diff --git a/src/soc/rockchip/rk3399/Makefile.inc b/src/soc/rockchip/rk3399/Makefile.inc
index 8448ff2..54c5115 100644
--- a/src/soc/rockchip/rk3399/Makefile.inc
+++ b/src/soc/rockchip/rk3399/Makefile.inc
@@ -66,6 +66,7 @@
 ramstage-y += clock.c
 ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += display.c
 ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += ../common/edp.c
+ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += mipi.c
 ramstage-y += ../common/gpio.c
 ramstage-y += gpio.c
 ramstage-y += ../common/i2c.c
diff --git a/src/soc/rockchip/rk3399/chip.h b/src/soc/rockchip/rk3399/chip.h
index 1f9462a..4b2ccc5 100644
--- a/src/soc/rockchip/rk3399/chip.h
+++ b/src/soc/rockchip/rk3399/chip.h
@@ -26,6 +26,18 @@
 	u32 bl_pwm_to_enable_udelay;
 	u32 framebuffer_bits_per_pixel;
 	u32 vop_mode;
+	u32 panel_pixel_clock;	/* below only be considered for MIPI displays */
+	u32 panel_refresh;
+	u32 panel_ha;
+	u32 panel_hbl;
+	u32 panel_hso;
+	u32 panel_hspw;
+	u32 panel_va;
+	u32 panel_vbl;
+	u32 panel_vso;
+	u32 panel_vspw;
+	u32 panel_display_on_mdelay;
+	u32 panel_video_mode_mdelay;
 };
 
 #endif /* __SOC_ROCKCHIP_RK3399_CHIP_H__ */
diff --git a/src/soc/rockchip/rk3399/clock.c b/src/soc/rockchip/rk3399/clock.c
index 7e205d2..35c96bc 100644
--- a/src/soc/rockchip/rk3399/clock.c
+++ b/src/soc/rockchip/rk3399/clock.c
@@ -953,3 +953,13 @@
 			      (src_clk_div - 1) <<
 			      CLK_PCLK_EDP_DIV_CON_SHIFT));
 }
+
+void rkclk_configure_mipi(void)
+{
+	/* Enable clk_mipidphy_ref and clk_mipidphy_cfg */
+	write32(&cru_ptr->clkgate_con[11],
+		RK_CLRBITS(1 << 14 | 1 << 15));
+	/* Enable pclk_mipi_dsi0 */
+	write32(&cru_ptr->clkgate_con[29],
+		RK_CLRBITS(1 << 1));
+}
diff --git a/src/soc/rockchip/rk3399/display.c b/src/soc/rockchip/rk3399/display.c
index c7ca891..379e1bf 100644
--- a/src/soc/rockchip/rk3399/display.c
+++ b/src/soc/rockchip/rk3399/display.c
@@ -31,6 +31,7 @@
 #include <soc/gpio.h>
 #include <soc/grf.h>
 #include <soc/mmu_operations.h>
+#include <soc/mipi.h>
 #include <soc/soc.h>
 #include <soc/vop.h>
 
@@ -47,6 +48,21 @@
 	printk(BIOS_WARNING, "Retrying epd initialization.\n");
 }
 
+static void rk_get_mipi_mode(struct edid *edid, device_t dev)
+{
+	struct soc_rockchip_rk3399_config *conf = dev->chip_info;
+
+	edid->mode.pixel_clock = conf->panel_pixel_clock;
+	edid->mode.refresh = conf->panel_refresh;
+	edid->mode.ha = conf->panel_ha;
+	edid->mode.hbl = conf->panel_hbl;
+	edid->mode.hso = conf->panel_hso;
+	edid->mode.hspw = conf->panel_hspw;
+	edid->mode.va = conf->panel_va;
+	edid->mode.vbl = conf->panel_vbl;
+	edid->mode.vso = conf->panel_vso;
+	edid->mode.vspw = conf->panel_vspw;
+}
 void rk_display_init(device_t dev)
 {
 	struct edid edid;
@@ -60,8 +76,6 @@
 	switch (conf->vop_mode) {
 	case VOP_MODE_NONE:
 		return;
-	case VOP_MODE_AUTO_DETECT:
-		/* try EDP first, then HDMI */
 	case VOP_MODE_EDP:
 		printk(BIOS_DEBUG, "Attempting to set up EDP display.\n");
 		rkclk_configure_vop_aclk(vop_id, 200 * MHz);
@@ -91,11 +105,22 @@
 			}
 		}
 		break;
-	case VOP_MODE_HDMI:
-		printk(BIOS_WARNING, "HDMI display is NOT supported yet.\n");
-		return;
+	case VOP_MODE_MIPI:
+		printk(BIOS_DEBUG, "Attempting to setup MIPI display.\n");
+
+		rkclk_configure_mipi();
+		rkclk_configure_vop_aclk(vop_id, 200 * MHz);
+
+		/* disable turnrequest turndisable forcetxstop forcerxmode */
+		write32(&rk3399_grf->soc_con22, RK_CLRBITS(0xffff));
+		/* select mipi-dsi0 signal from vop0 */
+		write32(&rk3399_grf->soc_con20, RK_CLRBITS(1 << 0));
+
+		rk_get_mipi_mode(&edid, dev);
+		detected_mode = VOP_MODE_MIPI;
+		break;
 	default:
-		printk(BIOS_WARNING, "Cannot read any EDID info, aborting.\n");
+		printk(BIOS_WARNING, "Unsupported vop_mode, aborting.\n");
 		return;
 	}
 
@@ -112,20 +137,21 @@
 	rkvop_prepare(vop_id, &edid);
 
 	switch (detected_mode) {
-	case VOP_MODE_HDMI:
-		/* should not be here before HDMI supported */
-		return;
+	case VOP_MODE_MIPI:
+		rk_mipi_prepare(&edid, conf->panel_display_on_mdelay, conf->panel_video_mode_mdelay);
+		break;
 	case VOP_MODE_EDP:
-	default:
 		/* will enable edp in depthcharge */
 		if (rk_edp_prepare()) {
 			reset_edp();
 			goto retry_edp; /* Rerun entire init sequence */
 		}
-		mainboard_power_on_backlight();
+		break;
+	default:
 		break;
 	}
-
+	mainboard_power_on_backlight();
 	set_vbe_mode_info_valid(&edid, (uintptr_t)0);
+
 	return;
 }
diff --git a/src/soc/rockchip/rk3399/include/soc/addressmap.h b/src/soc/rockchip/rk3399/include/soc/addressmap.h
index 1762a8d..7a365ad 100644
--- a/src/soc/rockchip/rk3399/include/soc/addressmap.h
+++ b/src/soc/rockchip/rk3399/include/soc/addressmap.h
@@ -59,6 +59,7 @@
 #define SARADC_BASE		0xff100000
 #define RK_PWM_BASE		0xff420000
 #define EDP_BASE		0xff970000
+#define MIPI_BASE		0xff960000
 
 #define VOP_BIG_BASE		0xff900000 /* corresponds to vop_id 0 */
 #define VOP_LIT_BASE		0xff8f0000 /* corresponds to vop_id 1 */
diff --git a/src/soc/rockchip/rk3399/include/soc/clock.h b/src/soc/rockchip/rk3399/include/soc/clock.h
index 3047f73..37a4c09 100644
--- a/src/soc/rockchip/rk3399/include/soc/clock.h
+++ b/src/soc/rockchip/rk3399/include/soc/clock.h
@@ -120,5 +120,6 @@
 int rkclk_was_watchdog_reset(void);
 uint32_t rkclk_i2c_clock_for_bus(unsigned bus);
 void rkclk_configure_edp(unsigned int hz);
+void rkclk_configure_mipi(void);
 
 #endif	/* __SOC_ROCKCHIP_RK3399_CLOCK_H__ */
diff --git a/src/soc/rockchip/rk3399/include/soc/mipi.h b/src/soc/rockchip/rk3399/include/soc/mipi.h
new file mode 100644
index 0000000..2dfbc52
--- /dev/null
+++ b/src/soc/rockchip/rk3399/include/soc/mipi.h
@@ -0,0 +1,288 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2017 Rockchip Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __RK_MIPI_H
+#define __RK_MIPI_H
+
+#include <stdlib.h>
+
+struct rk_mipi_regs {
+	u32 dsi_version;
+	u32 dsi_pwr_up;
+	u32 dsi_clk_cfg;
+	u32 dsi_dpi_vcid;
+	u32 dsi_dpi_color_coding;
+	u32 dsi_dpi_cfg_pol;
+	u32 dsi_dpi_lp_cmd_tim;
+	u8 reserved0[0x28 - 0x18];
+	u32 dsi_pckhdl_cfg;
+	u8 reserved1[0x30 - 0x2c];
+	u32 dsi_mode_cfg;
+	u32 dsi_vid_mode_cfg;
+	u32 dsi_vid_pkt_size;
+	u32 dsi_vid_num_chumks;
+	u32 dsi_vid_null_pkt_size;
+	u32 dsi_vid_hsa_time;
+	u32 dsi_vid_hbp_time;
+	u32 dsi_vid_hline_time;
+	u32 dsi_vid_vsa_lines;
+	u32 dsi_vid_vbp_lines;
+	u32 dsi_vid_vfp_lines;
+	u32 dsi_vid_vactive_lines;
+	u32 dsi_edpi_cmd_size;
+	u32 dsi_cmd_mode_cfg;
+	u32 dsi_gen_hdr;
+	u32 dsi_gen_pld_data;
+	u32 dsi_cmd_pkt_status;
+	u32 dsi_to_cnt_cfg;
+	u8 reserved2[0x88 - 0x78];
+	u32 dsi_bta_to_cnt;
+	u32 reserved3;
+	u32 dsi_lpclk_ctrl;
+	u32 dsi_phy_tmr_lpclk_cfg;
+	u32 dsi_phy_tmr_cfg;
+	u32 dsi_phy_rstz;
+	u32 dsi_phy_if_cfg;
+	u8 reserved4[0xac - 0xa4];
+	u32 dsi_phy_status;
+	u32 dsi_phy_tst_ctrl0;
+	u32 dsi_phy_tst_ctrl1;
+	u32 dsi_int_st0;
+	u32 dsi_int_st1;
+	u32 dsi_int_msk0;
+	u32 dsi_int_msk1;
+};
+check_member(rk_mipi_regs, dsi_int_msk1, 0xc8);
+
+#define RESET				0
+#define POWERUP				BIT(0)
+
+#define TO_CLK_DIVIDSION(div)		(((div) & 0xff) << 8)
+#define TX_ESC_CLK_DIVIDSION(div)	(((div) & 0xff) << 0)
+
+#define EN18_LOOSELY			BIT(8)
+#define DPI_COLOR_CODING_16BIT_1	0x0
+#define DPI_COLOR_CODING_16BIT_2	0x1
+#define DPI_COLOR_CODING_16BIT_3	0x2
+#define DPI_COLOR_CODING_18BIT_1	0x3
+#define DPI_COLOR_CODING_18BIT_2	0x4
+#define DPI_COLOR_CODING_24BIT		0x5
+
+#define COLORM_ACTIVE_LOW		BIT(4)
+#define SHUTD_ACTIVE_LOW		BIT(3)
+#define HSYNC_ACTIVE_LOW		BIT(2)
+#define VSYNC_ACTIVE_LOW		BIT(1)
+#define DATAEN_ACTIVE_LOW		BIT(0)
+
+#define OUTVACT_LPCMD_TIME(p)		(((p) & 0xff) << 16)
+#define INVACT_LPCMD_TIME(p)		((p) & 0xff)
+
+#define EN_CRC_RX			BIT(4)
+#define EN_ECC_RX			BIT(3)
+#define EN_BTA				BIT(2)
+#define EN_EOTP_RX			BIT(1)
+#define EN_EOTP_TX			BIT(0)
+
+#define ENABLE_VIDEO_MODE		0
+#define ENABLE_CMD_MODE			BIT(0)
+
+#define FRAME_BTA_ACK			BIT(14)
+#define ENABLE_LOW_POWER		(0x3f << 8)
+#define ENABLE_LOW_POWER_MASK		(0x3f << 8)
+#define VID_MODE_TYPE_BURST_SYNC_PULSES	0x0
+#define VID_MODE_TYPE_BURST_SYNC_EVENTS	0x1
+#define VID_MODE_TYPE_BURST		0x2
+
+#define VID_PKT_SIZE(p)			(((p) & 0x3fff) << 0)
+#define VID_PKT_MAX_SIZE		0x3fff
+
+#define MAX_RD_PKT_SIZE_LP		BIT(24)
+#define DCS_LW_TX_LP			BIT(19)
+#define DCS_SR_0P_TX_LP			BIT(18)
+#define DCS_SW_1P_TX_LP			BIT(17)
+#define DCS_SW_0P_TX_LP			BIT(16)
+#define GEN_LW_TX_LP			BIT(14)
+#define GEN_SR_2P_TX_LP			BIT(13)
+#define GEN_SR_1P_TX_LP			BIT(12)
+#define GEN_SR_0P_TX_LP			BIT(11)
+#define GEN_SW_2P_TX_LP			BIT(10)
+#define GEN_SW_1P_TX_LP			BIT(9)
+#define GEN_SW_0P_TX_LP			BIT(8)
+#define EN_ACK_RQST			BIT(1)
+#define EN_TEAR_FX			BIT(0)
+
+#define CMD_MODE_ALL_LP			(MAX_RD_PKT_SIZE_LP | \
+					 DCS_LW_TX_LP | \
+					 DCS_SR_0P_TX_LP | \
+					 DCS_SW_1P_TX_LP | \
+					 DCS_SW_0P_TX_LP | \
+					 GEN_LW_TX_LP | \
+					 GEN_SR_2P_TX_LP | \
+					 GEN_SR_1P_TX_LP | \
+					 GEN_SR_0P_TX_LP | \
+					 GEN_SW_2P_TX_LP | \
+					 GEN_SW_1P_TX_LP | \
+					 GEN_SW_0P_TX_LP)
+
+#define GEN_HDATA(data)			(((data) & 0xffff) << 8)
+#define GEN_HDATA_MASK			(0xffff << 8)
+#define GEN_HTYPE(type)			(((type) & 0xff) << 0)
+#define GEN_HTYPE_MASK			0xff
+
+#define HSTX_TO_CNT(p)			(((p) & 0xffff) << 16)
+#define LPRX_TO_CNT(p)			((p) & 0xffff)
+
+#define AUTO_CLKLANE_CTRL		BIT(1)
+#define PHY_TXREQUESTCLKHS		BIT(0)
+
+#define PHY_CLKHS2LP_TIME(lbcc)		(((lbcc) & 0x3ff) << 16)
+#define PHY_CLKLP2HS_TIME(lbcc)		((lbcc) & 0x3ff)
+
+#define PHY_HS2LP_TIME(lbcc)		(((lbcc) & 0xff) << 24)
+#define PHY_LP2HS_TIME(lbcc)		(((lbcc) & 0xff) << 16)
+#define MAX_RD_TIME(lbcc)		((lbcc) & 0x7fff)
+
+#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 N_LANES(n)			((((n) - 1) & 0x3) << 0)
+#define PHY_STOP_WAIT_TIME(cycle)	(((cycle) & 0xff) << 8)
+
+#define LOCK				BIT(0)
+#define STOP_STATE_CLK_LANE		BIT(2)
+
+#define PHY_TESTCLK			BIT(1)
+#define PHY_UNTESTCLK			0
+#define PHY_TESTCLR			BIT(0)
+#define PHY_UNTESTCLR			0
+
+#define PHY_TESTEN			BIT(16)
+#define PHY_UNTESTEN			0
+#define PHY_TESTDOUT(n)			(((n) & 0xff) << 8)
+#define PHY_TESTDIN(n)			(((n) & 0xff) << 0)
+
+#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_3MA		BIT(3)
+#define CP_PROGRAM_EN		BIT(7)
+#define LPF_PROGRAM_EN		BIT(6)
+#define LPF_RESISTORS_20_KOHM	0
+
+#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) & 0x1f)
+#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_0 0x44
+
+enum mipi_dsi_pixel_format {
+	MIPI_DSI_FMT_RGB888,
+	MIPI_DSI_FMT_RGB666,
+	MIPI_DSI_FMT_RGB666_PACKED,
+	MIPI_DSI_FMT_RGB565,
+};
+
+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,
+};
+
+enum rk_mipi_dsi_mode {
+	MIPI_DSI_CMD_MODE,
+	MIPI_DSI_VID_MODE,
+};
+
+enum {
+	MIPI_DCS_NOP = 0x00,
+	MIPI_DCS_EXIT_SLEEP_MODE = 0x11,
+	MIPI_DCS_SET_DISPLAY_ON = 0x29,
+};
+
+struct dphy_pll_testdin_map {
+	unsigned int max_mbps;
+	u8 testdin;
+};
+
+struct rk_mipi_dsi {
+	u64 lane_bps; /* per lane */
+	u32 lanes;
+	u32 format;
+	u16 input_div;
+	u16 feedback_div;
+};
+
+void rk_mipi_prepare(const struct edid *edid, u32 display_on_mdelay, u32 video_mode_mdelay);
+#endif
diff --git a/src/soc/rockchip/rk3399/mipi.c b/src/soc/rockchip/rk3399/mipi.c
new file mode 100644
index 0000000..5c8f4c2
--- /dev/null
+++ b/src/soc/rockchip/rk3399/mipi.c
@@ -0,0 +1,436 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2017 Rockchip Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <arch/io.h>
+#include <assert.h>
+#include <console/console.h>
+#include <delay.h>
+#include <device/device.h>
+#include <edid.h>
+#include <gpio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <soc/addressmap.h>
+#include <soc/clock.h>
+#include <soc/display.h>
+#include <soc/mipi.h>
+#include <soc/soc.h>
+#include <timer.h>
+
+static struct rk_mipi_dsi rk_mipi;
+static struct rk_mipi_regs *mipi_regs = (void *)MIPI_BASE;
+
+/*
+ * The controller should generate 2 frames before
+ * preparing the peripheral.
+ */
+static void rk_mipi_dsi_wait_for_two_frames(struct rk_mipi_dsi *dsi,
+					    const struct edid *edid)
+{
+	int two_frames;
+	unsigned int refresh = edid->mode.refresh;
+
+	two_frames = div_round_up(MSECS_PER_SEC * 2, refresh);
+	mdelay(two_frames);
+}
+
+static const struct dphy_pll_testdin_map dptdin_map[] = {
+	{  90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
+	{ 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
+	{ 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
+	{ 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
+	{ 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
+	{ 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
+	{ 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
+	{1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
+	{1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
+	{1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
+};
+
+static int max_mbps_to_testdin(unsigned int max_mbps)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dptdin_map); i++)
+		if (dptdin_map[i].max_mbps > max_mbps)
+			return dptdin_map[i].testdin;
+
+	return -1;
+}
+
+static void rk_mipi_dsi_phy_write(struct rk_mipi_dsi *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.
+	 */
+	write32(&mipi_regs->dsi_phy_tst_ctrl0, PHY_TESTCLK | PHY_UNTESTCLR);
+
+	write32(&mipi_regs->dsi_phy_tst_ctrl1, PHY_TESTEN | PHY_TESTDOUT(0) |
+					       PHY_TESTDIN(test_code));
+
+	write32(&mipi_regs->dsi_phy_tst_ctrl0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+
+	write32(&mipi_regs->dsi_phy_tst_ctrl1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
+					       PHY_TESTDIN(test_data));
+
+	write32(&mipi_regs->dsi_phy_tst_ctrl0, PHY_TESTCLK | PHY_UNTESTCLR);
+}
+
+static int rk_mipi_dsi_phy_init(struct rk_mipi_dsi *dsi)
+{
+	int ret, testdin, vco;
+
+	int lane_mbps = div_round_up(dsi->lane_bps, USECS_PER_SEC);
+	vco = (lane_mbps < 200) ? 0 : (lane_mbps + 100) / 200;
+
+	testdin = max_mbps_to_testdin(lane_mbps);
+	if (testdin < 0) {
+		printk(BIOS_DEBUG, "failed to get testdin for %dmbps\n",
+		       lane_mbps);
+		return testdin;
+	}
+
+	/* Start by clearing PHY state */
+	write32(&mipi_regs->dsi_phy_tst_ctrl0, PHY_UNTESTCLR);
+	write32(&mipi_regs->dsi_phy_tst_ctrl0, PHY_TESTCLR);
+	write32(&mipi_regs->dsi_phy_tst_ctrl0, PHY_UNTESTCLR);
+
+	rk_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);
+
+	rk_mipi_dsi_phy_write(dsi, PLL_CP_CONTROL_PLL_LOCK_BYPASS,
+			      CP_CURRENT_3MA);
+	rk_mipi_dsi_phy_write(dsi, PLL_LPF_AND_CP_CONTROL,
+			      CP_PROGRAM_EN |
+			      LPF_PROGRAM_EN |
+			      LPF_RESISTORS_20_KOHM);
+
+	rk_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0,
+			      HSFREQRANGE_SEL(testdin));
+	rk_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO,
+			      INPUT_DIVIDER(dsi->input_div));
+	rk_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
+			      LOOP_DIV_LOW_SEL(dsi->feedback_div) |
+			      LOW_PROGRAM_EN);
+	rk_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
+			      LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
+			      HIGH_PROGRAM_EN);
+	rk_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
+			      PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
+	rk_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
+			      LOW_PROGRAM_EN |
+			      BIASEXTR_SEL(BIASEXTR_127_7));
+	rk_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
+			      HIGH_PROGRAM_EN |
+			      BANDGAP_SEL(BANDGAP_96_10));
+	rk_mipi_dsi_phy_write(dsi, BANDGAP_AND_BIAS_CONTROL,
+			      POWER_CONTROL | INTERNAL_REG_CURRENT |
+			      BIAS_BLOCK_ON | BANDGAP_ON);
+	rk_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL,
+			      TER_RESISTOR_LOW | TER_CAL_DONE |
+			      SETRD_MAX | TER_RESISTORS_ON);
+	rk_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL,
+			      TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON |
+			      SETRD_MAX | POWER_MANAGE |
+			      TER_RESISTORS_ON);
+
+	write32(&mipi_regs->dsi_phy_rstz, PHY_ENFORCEPLL | PHY_ENABLECLK |
+					  PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
+	return ret;
+}
+
+static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
+{
+	switch (fmt) {
+	case MIPI_DSI_FMT_RGB888:
+	case MIPI_DSI_FMT_RGB666:
+		return 24;
+
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		return 18;
+
+	case MIPI_DSI_FMT_RGB565:
+		return 16;
+	}
+
+	return -1;
+}
+
+static int rk_mipi_dsi_get_lane_bps(struct rk_mipi_dsi *dsi,
+				    const struct edid *edid)
+{
+	u32 i, pre;
+	u64 pclk, pllref, tmp, target_bps;
+	u32 m = 1, n = 1;
+	u32 max_bps = 1500 * MHz;
+	int bpp;
+
+	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+	if (bpp < 0) {
+		printk(BIOS_DEBUG, "failed to get bpp for pixel format %d\n",
+		       dsi->format);
+		return bpp;
+	}
+	pclk = edid->mode.pixel_clock * MSECS_PER_SEC;
+	/* take 1 / 0.8, since mbps must bigger than bandwidth of RGB */
+	target_bps = pclk / dsi->lanes * bpp / 8 * 10;
+	if (target_bps >= max_bps) {
+		printk(BIOS_DEBUG, "DPHY clock frequency is out of range\n");
+		return -1;
+	}
+	pllref = OSC_HZ;
+	tmp = pllref;
+	/*
+	 * The limits on the PLL divisor are:
+	 *
+	 *	5MHz <= (pllref / n) <= 40MHz
+	 */
+	for (i = pllref / (5 * MHz); i > div_round_up(pllref, 40 * MHz); i--) {
+		pre = pllref / i;
+		if ((tmp > (target_bps % pre)) && (target_bps / pre < 512)) {
+			tmp = target_bps % pre;
+			n = i;
+			m = target_bps / pre;
+		}
+		if (tmp == 0)
+			break;
+	}
+	dsi->lane_bps = pllref / n * m;
+	dsi->input_div = n;
+	dsi->feedback_div = m;
+
+	return 0;
+}
+
+static void rk_mipi_dsi_dpi_config(struct rk_mipi_dsi *dsi)
+{
+	u32 color = 0;
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		color = DPI_COLOR_CODING_24BIT;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		color = DPI_COLOR_CODING_18BIT_1;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		color = DPI_COLOR_CODING_16BIT_1;
+		break;
+	}
+
+	write32(&mipi_regs->dsi_dpi_vcid, 0);
+	write32(&mipi_regs->dsi_dpi_color_coding, color);
+
+	write32(&mipi_regs->dsi_dpi_cfg_pol, 0);
+
+	write32(&mipi_regs->dsi_dpi_lp_cmd_tim, OUTVACT_LPCMD_TIME(4) |
+						INVACT_LPCMD_TIME(4));
+}
+
+static void rk_mipi_dsi_packet_handler_config(struct rk_mipi_dsi *dsi)
+{
+	write32(&mipi_regs->dsi_pckhdl_cfg, EN_CRC_RX | EN_ECC_RX | EN_BTA);
+}
+
+static void rk_mipi_dsi_video_mode_config(struct rk_mipi_dsi *dsi)
+{
+	write32(&mipi_regs->dsi_vid_mode_cfg,
+		VID_MODE_TYPE_BURST_SYNC_PULSES | ENABLE_LOW_POWER);
+}
+
+static void rk_mipi_dsi_video_packet_config(struct rk_mipi_dsi *dsi)
+{
+	write32(&mipi_regs->dsi_vid_pkt_size, VID_PKT_SIZE(0x300));
+}
+
+static void rk_mipi_dsi_command_mode_config(struct rk_mipi_dsi *dsi)
+{
+	write32(&mipi_regs->dsi_to_cnt_cfg,
+		HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
+	write32(&mipi_regs->dsi_bta_to_cnt, 0xd00);
+	write32(&mipi_regs->dsi_cmd_mode_cfg, CMD_MODE_ALL_LP);
+	write32(&mipi_regs->dsi_mode_cfg, ENABLE_CMD_MODE);
+}
+
+/* Get lane byte clock cycles. */
+static u32 rk_mipi_dsi_get_hcomponent_lbcc(struct rk_mipi_dsi *dsi,
+					   u32 hcomponent,
+					   const struct edid *edid)
+{
+	u32 lbcc;
+	u64 lbcc_tmp;
+
+	lbcc_tmp = hcomponent * dsi->lane_bps / (8 * MSECS_PER_SEC);
+	lbcc = div_round_up(lbcc_tmp, edid->mode.pixel_clock);
+
+	return lbcc;
+}
+
+static void rk_mipi_dsi_line_timer_config(struct rk_mipi_dsi *dsi,
+					  const struct edid *edid)
+{
+	u32 htotal, hsa, hbp, lbcc;
+
+	htotal = edid->mode.ha + edid->mode.hbl;
+	hsa = edid->mode.hspw;
+	hbp = edid->mode.hbl - edid->mode.hso - edid->mode.hspw;
+
+	lbcc = rk_mipi_dsi_get_hcomponent_lbcc(dsi, htotal, edid);
+	write32(&mipi_regs->dsi_vid_hline_time, lbcc);
+
+	lbcc = rk_mipi_dsi_get_hcomponent_lbcc(dsi, hsa, edid);
+	write32(&mipi_regs->dsi_vid_hsa_time, lbcc);
+	lbcc = rk_mipi_dsi_get_hcomponent_lbcc(dsi, hbp, edid);
+	write32(&mipi_regs->dsi_vid_hbp_time, lbcc);
+}
+
+static void rk_mipi_dsi_vertical_timing_config(struct rk_mipi_dsi *dsi,
+					       const struct edid *edid)
+{
+	u32 vactive, vsa, vfp, vbp;
+
+	vactive = edid->mode.va;
+	vsa = edid->mode.vspw;
+	vfp = edid->mode.vso;
+	vbp = edid->mode.vbl - edid->mode.vso - edid->mode.vspw;
+
+	write32(&mipi_regs->dsi_vid_vactive_lines, vactive);
+	write32(&mipi_regs->dsi_vid_vsa_lines, vsa);
+	write32(&mipi_regs->dsi_vid_vfp_lines, vfp);
+	write32(&mipi_regs->dsi_vid_vbp_lines, vbp);
+}
+
+static void rk_mipi_dsi_dphy_timing_config(struct rk_mipi_dsi *dsi)
+{
+	/*
+	 * HS-PREPARE: 40ns + 4 * UI ~ 85ns + 6 * UI
+	 * HS-EXIT: 100ns
+	 */
+	write32(&mipi_regs->dsi_phy_tmr_cfg, PHY_HS2LP_TIME(0x40) |
+					     PHY_LP2HS_TIME(0x40) |
+					     MAX_RD_TIME(10000));
+
+	write32(&mipi_regs->dsi_phy_tmr_lpclk_cfg, PHY_CLKHS2LP_TIME(0x40) |
+						   PHY_CLKLP2HS_TIME(0x40));
+}
+
+static void rk_mipi_dsi_clear_err(struct rk_mipi_dsi *dsi)
+{
+	read32(&mipi_regs->dsi_int_st0);
+	read32(&mipi_regs->dsi_int_st1);
+	write32(&mipi_regs->dsi_int_msk0, 0);
+	write32(&mipi_regs->dsi_int_msk1, 0);
+}
+
+static void rk_mipi_dsi_dphy_interface_config(struct rk_mipi_dsi *dsi)
+{
+	write32(&mipi_regs->dsi_phy_if_cfg, PHY_STOP_WAIT_TIME(0x20) |
+					    N_LANES(dsi->lanes));
+}
+
+static void rk_mipi_dsi_set_mode(struct rk_mipi_dsi *dsi,
+				 enum rk_mipi_dsi_mode mode)
+{
+	write32(&mipi_regs->dsi_pwr_up, RESET);
+	if (mode == MIPI_DSI_CMD_MODE) {
+		write32(&mipi_regs->dsi_mode_cfg, ENABLE_CMD_MODE);
+	} else {
+		write32(&mipi_regs->dsi_mode_cfg, ENABLE_VIDEO_MODE);
+		rk_mipi_dsi_video_mode_config(dsi);
+		write32(&mipi_regs->dsi_lpclk_ctrl, PHY_TXREQUESTCLKHS);
+	}
+	write32(&mipi_regs->dsi_pwr_up, POWERUP);
+}
+
+static void rk_mipi_dsi_init(struct rk_mipi_dsi *dsi)
+{
+	/*
+	 * The maximum permitted escape clock is 20MHz and it is derived from
+	 * lanebyteclk, which is running at "lane_mbps / 8".  Thus we want:
+	 *
+	 *     (lane_mbps >> 3) / esc_clk_division < 20
+	 * which is:
+	 *     (lane_mbps >> 3) / 20 > esc_clk_division
+	 */
+	u32 esc_clk_division = div_round_up(dsi->lane_bps, 8 * 20 * USECS_PER_SEC);
+
+	write32(&mipi_regs->dsi_pwr_up, RESET);
+	write32(&mipi_regs->dsi_phy_rstz, PHY_DISFORCEPLL | PHY_DISABLECLK |
+					  PHY_RSTZ | PHY_SHUTDOWNZ);
+	write32(&mipi_regs->dsi_clk_cfg,
+		TO_CLK_DIVIDSION(10) |
+		TX_ESC_CLK_DIVIDSION(esc_clk_division));
+}
+
+static int rk_mipi_dsi_dcs_transfer(struct rk_mipi_dsi *dsi, u32 hdr_val)
+{
+	int ret;
+
+	hdr_val = GEN_HDATA(hdr_val) | GEN_HTYPE(0x05);
+	ret = read32(&mipi_regs->dsi_cmd_pkt_status);
+	if (ret < 0) {
+		printk(BIOS_DEBUG, "failed to get available command FIFO\n");
+		return ret;
+	}
+
+	write32(&mipi_regs->dsi_lpclk_ctrl, 0);
+	write32(&mipi_regs->dsi_cmd_mode_cfg, CMD_MODE_ALL_LP);
+	write32(&mipi_regs->dsi_gen_hdr, hdr_val);
+
+	return 0;
+}
+
+void rk_mipi_prepare(const struct edid *edid, u32 display_on_mdelay, u32 video_mode_mdelay)
+{
+	rk_mipi.lanes = 4;
+	rk_mipi.format = MIPI_DSI_FMT_RGB888;
+	if (rk_mipi_dsi_get_lane_bps(&rk_mipi, edid) < 0)
+		return;
+
+	rk_mipi_dsi_init(&rk_mipi);
+	rk_mipi_dsi_dpi_config(&rk_mipi);
+	rk_mipi_dsi_packet_handler_config(&rk_mipi);
+	rk_mipi_dsi_video_mode_config(&rk_mipi);
+	rk_mipi_dsi_video_packet_config(&rk_mipi);
+	rk_mipi_dsi_command_mode_config(&rk_mipi);
+	rk_mipi_dsi_line_timer_config(&rk_mipi, edid);
+	rk_mipi_dsi_vertical_timing_config(&rk_mipi, edid);
+	rk_mipi_dsi_dphy_timing_config(&rk_mipi);
+	rk_mipi_dsi_dphy_interface_config(&rk_mipi);
+	rk_mipi_dsi_clear_err(&rk_mipi);
+	rk_mipi_dsi_phy_init(&rk_mipi);
+	rk_mipi_dsi_wait_for_two_frames(&rk_mipi, edid);
+
+	rk_mipi_dsi_set_mode(&rk_mipi, MIPI_DSI_CMD_MODE);
+	if (rk_mipi_dsi_dcs_transfer(&rk_mipi, MIPI_DCS_EXIT_SLEEP_MODE) < 0)
+		return;
+	mdelay(display_on_mdelay);
+	if (rk_mipi_dsi_dcs_transfer(&rk_mipi, MIPI_DCS_SET_DISPLAY_ON) < 0)
+		return;
+	mdelay(video_mode_mdelay);
+
+	rk_mipi_dsi_set_mode(&rk_mipi, MIPI_DSI_VID_MODE);
+}

-- 
To view, visit https://review.coreboot.org/19477
To unsubscribe, visit https://review.coreboot.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I02475eefb187c619c614b1cd20e97074bc8d917f
Gerrit-PatchSet: 11
Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Owner: nickey.yang at rock-chips.com
Gerrit-Reviewer: Julius Werner <jwerner at chromium.org>
Gerrit-Reviewer: Paul Menzel <paulepanter at users.sourceforge.net>
Gerrit-Reviewer: Sean Paul <seanpaul at chromium.org>
Gerrit-Reviewer: Shunqian Zheng <zhengsq at rock-chips.com>
Gerrit-Reviewer: build bot (Jenkins) <no-reply at coreboot.org>
Gerrit-Reviewer: nickey.yang at rock-chips.com



More information about the coreboot-gerrit mailing list