summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYe Li <ye.li@nxp.com>2018-07-17 20:35:11 -0700
committerYe Li <ye.li@nxp.com>2018-07-18 22:19:30 -0700
commitb08b17cde486a232b602b806862de56a26117c51 (patch)
tree9ad7d3807346e5a7bc99abea4934f3f0ee185add
parentfca13cf24f4a7be15747d92e4622d5e3749f80ef (diff)
MLK-18945-6 video: Add driver sec_mipi_dsim for mipi dsi on iMX8MM and iMX7D
Add new mipi dsi driver sec_mipi_dsim to support the samsung mipi dsi used on iMX8MM and iMX7D platforms. This driver implements the interfaces required by mipi dsi bridge. Users can use mipi dsi bridge common APIs to access it. Signed-off-by: Ye Li <ye.li@nxp.com>
-rw-r--r--drivers/video/Kconfig8
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/sec_mipi_dsim.c905
-rw-r--r--include/sec_mipi_dsim.h20
4 files changed, 934 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 9ae1c3d3de..1613e5737a 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -493,6 +493,14 @@ config IMX_NORTHWEST_MIPI_DSI
help
Support for i.MX7ULP MIPI DSI controller.
+config IMX_SEC_MIPI_DSI
+ bool "i.MX Samsung's MIPI DSI"
+ default n
+ select IMX_MIPI_DSI_BRIDGE
+ depends on VIDEO && (IMX8MM || MX7)
+ help
+ Support for i.MX7 and i.MX8MM MIPI DSI controller.
+
config MXC_EPDC
bool "i.MX EPDC support"
depends on LCD && (MX7 || MX6)
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index d8db5d2b19..306d4e4db6 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_CSI) += mxc_csi.o
obj-$(CONFIG_VIDEO_PXP) += mxc_pxp.o
obj-$(CONFIG_VIDEO_GIS) += mxc_gis.o
obj-$(CONFIG_IMX_NORTHWEST_MIPI_DSI) += mipi_dsi_northwest.o
+obj-$(CONFIG_IMX_SEC_MIPI_DSI) += sec_mipi_dsim.o
obj-$(CONFIG_IMX_MIPI_DSI_BRIDGE) += imx_mipi_dsi_bridge.o
obj-$(CONFIG_HX8363) += hx8363_wvga.o
diff --git a/drivers/video/sec_mipi_dsim.c b/drivers/video/sec_mipi_dsim.c
new file mode 100644
index 0000000000..533c910990
--- /dev/null
+++ b/drivers/video/sec_mipi_dsim.c
@@ -0,0 +1,905 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/arch/sys_proto.h>
+#include <div64.h>
+#include <sec_mipi_dsim.h>
+#include <imx_mipi_dsi_bridge.h>
+
+#define MIPI_FIFO_TIMEOUT 250000 /* 250ms */
+
+#define DRIVER_NAME "imx_sec_mipi_dsim"
+
+/* dsim registers */
+#define DSIM_VERSION 0x00
+#define DSIM_STATUS 0x04
+#define DSIM_RGB_STATUS 0x08
+#define DSIM_SWRST 0x0c
+#define DSIM_CLKCTRL 0x10
+#define DSIM_TIMEOUT 0x14
+#define DSIM_CONFIG 0x18
+#define DSIM_ESCMODE 0x1c
+#define DSIM_MDRESOL 0x20
+#define DSIM_MVPORCH 0x24
+#define DSIM_MHPORCH 0x28
+#define DSIM_MSYNC 0x2c
+#define DSIM_SDRESOL 0x30
+#define DSIM_INTSRC 0x34
+#define DSIM_INTMSK 0x38
+
+/* packet */
+#define DSIM_PKTHDR 0x3c
+#define DSIM_PAYLOAD 0x40
+#define DSIM_RXFIFO 0x44
+#define DSIM_FIFOTHLD 0x48
+#define DSIM_FIFOCTRL 0x4c
+#define DSIM_MEMACCHR 0x50
+#define DSIM_MULTI_PKT 0x78
+
+/* pll control */
+#define DSIM_PLLCTRL_1G 0x90
+#define DSIM_PLLCTRL 0x94
+#define DSIM_PLLCTRL1 0x98
+#define DSIM_PLLCTRL2 0x9c
+#define DSIM_PLLTMR 0xa0
+
+/* dphy */
+#define DSIM_PHYTIMING 0xb4
+#define DSIM_PHYTIMING1 0xb8
+#define DSIM_PHYTIMING2 0xbc
+
+/* reg bit manipulation */
+#define REG_MASK(e, s) (((1 << ((e) - (s) + 1)) - 1) << (s))
+#define REG_PUT(x, e, s) (((x) << (s)) & REG_MASK(e, s))
+#define REG_GET(x, e, s) (((x) & REG_MASK(e, s)) >> (s))
+
+/* register bit fields */
+#define STATUS_PLLSTABLE BIT(31)
+#define STATUS_SWRSTRLS BIT(20)
+#define STATUS_TXREADYHSCLK BIT(10)
+#define STATUS_ULPSCLK BIT(9)
+#define STATUS_STOPSTATECLK BIT(8)
+#define STATUS_GET_ULPSDAT(x) REG_GET(x, 7, 4)
+#define STATUS_GET_STOPSTATEDAT(x) REG_GET(x, 3, 0)
+
+#define RGB_STATUS_CMDMODE_INSEL BIT(31)
+#define RGB_STATUS_GET_RGBSTATE(x) REG_GET(x, 12, 0)
+
+#define CLKCTRL_TXREQUESTHSCLK BIT(31)
+#define CLKCTRL_DPHY_SEL_1G BIT(29)
+#define CLKCTRL_DPHY_SEL_1P5G (0x0 << 29)
+#define CLKCTRL_ESCCLKEN BIT(28)
+#define CLKCTRL_PLLBYPASS BIT(29)
+#define CLKCTRL_BYTECLKSRC_DPHY_PLL REG_PUT(0, 26, 25)
+#define CLKCTRL_BYTECLKEN BIT(24)
+#define CLKCTRL_SET_LANEESCCLKEN(x) REG_PUT(x, 23, 19)
+#define CLKCTRL_SET_ESCPRESCALER(x) REG_PUT(x, 15, 0)
+
+#define TIMEOUT_SET_BTAOUT(x) REG_PUT(x, 23, 16)
+#define TIMEOUT_SET_LPDRTOUT(x) REG_PUT(x, 15, 0)
+
+#define CONFIG_NON_CONTINOUS_CLOCK_LANE BIT(31)
+#define CONFIG_CLKLANE_STOP_START BIT(30)
+#define CONFIG_MFLUSH_VS BIT(29)
+#define CONFIG_EOT_R03 BIT(28)
+#define CONFIG_SYNCINFORM BIT(27)
+#define CONFIG_BURSTMODE BIT(26)
+#define CONFIG_VIDEOMODE BIT(25)
+#define CONFIG_AUTOMODE BIT(24)
+#define CONFIG_HSEDISABLEMODE BIT(23)
+#define CONFIG_HFPDISABLEMODE BIT(22)
+#define CONFIG_HBPDISABLEMODE BIT(21)
+#define CONFIG_HSADISABLEMODE BIT(20)
+#define CONFIG_SET_MAINVC(x) REG_PUT(x, 19, 18)
+#define CONFIG_SET_SUBVC(x) REG_PUT(x, 17, 16)
+#define CONFIG_SET_MAINPIXFORMAT(x) REG_PUT(x, 14, 12)
+#define CONFIG_SET_SUBPIXFORMAT(x) REG_PUT(x, 10, 8)
+#define CONFIG_SET_NUMOFDATLANE(x) REG_PUT(x, 6, 5)
+#define CONFIG_SET_LANEEN(x) REG_PUT(x, 4, 0)
+
+#define MDRESOL_MAINSTANDBY BIT(31)
+#define MDRESOL_SET_MAINVRESOL(x) REG_PUT(x, 27, 16)
+#define MDRESOL_SET_MAINHRESOL(x) REG_PUT(x, 11, 0)
+
+#define MVPORCH_SET_CMDALLOW(x) REG_PUT(x, 31, 28)
+#define MVPORCH_SET_STABLEVFP(x) REG_PUT(x, 26, 16)
+#define MVPORCH_SET_MAINVBP(x) REG_PUT(x, 10, 0)
+
+#define MHPORCH_SET_MAINHFP(x) REG_PUT(x, 31, 16)
+#define MHPORCH_SET_MAINHBP(x) REG_PUT(x, 15, 0)
+
+#define MSYNC_SET_MAINVSA(x) REG_PUT(x, 31, 22)
+#define MSYNC_SET_MAINHSA(x) REG_PUT(x, 15, 0)
+
+#define INTSRC_PLLSTABLE BIT(31)
+#define INTSRC_SWRSTRELEASE BIT(30)
+#define INTSRC_SFRPLFIFOEMPTY BIT(29)
+#define INTSRC_SFRPHFIFOEMPTY BIT(28)
+#define INTSRC_FRAMEDONE BIT(24)
+#define INTSRC_LPDRTOUT BIT(21)
+#define INTSRC_TATOUT BIT(20)
+#define INTSRC_RXDATDONE BIT(18)
+#define INTSRC_MASK (INTSRC_PLLSTABLE | \
+ INTSRC_SWRSTRELEASE | \
+ INTSRC_SFRPLFIFOEMPTY | \
+ INTSRC_SFRPHFIFOEMPTY | \
+ INTSRC_FRAMEDONE | \
+ INTSRC_LPDRTOUT | \
+ INTSRC_TATOUT | \
+ INTSRC_RXDATDONE)
+
+#define INTMSK_MSKPLLSTABLE BIT(31)
+#define INTMSK_MSKSWRELEASE BIT(30)
+#define INTMSK_MSKSFRPLFIFOEMPTY BIT(29)
+#define INTMSK_MSKSFRPHFIFOEMPTY BIT(28)
+#define INTMSK_MSKFRAMEDONE BIT(24)
+#define INTMSK_MSKLPDRTOUT BIT(21)
+#define INTMSK_MSKTATOUT BIT(20)
+#define INTMSK_MSKRXDATDONE BIT(18)
+
+#define PLLCTRL_DPDNSWAP_CLK BIT(25)
+#define PLLCTRL_DPDNSWAP_DAT BIT(24)
+#define PLLCTRL_PLLEN BIT(23)
+#define PLLCTRL_SET_PMS(x) REG_PUT(x, 19, 1)
+
+#define PHYTIMING_SET_M_TLPXCTL(x) REG_PUT(x, 15, 8)
+#define PHYTIMING_SET_M_THSEXITCTL(x) REG_PUT(x, 7, 0)
+
+#define PHYTIMING1_SET_M_TCLKPRPRCTL(x) REG_PUT(x, 31, 24)
+#define PHYTIMING1_SET_M_TCLKZEROCTL(x) REG_PUT(x, 23, 16)
+#define PHYTIMING1_SET_M_TCLKPOSTCTL(x) REG_PUT(x, 15, 8)
+#define PHYTIMING1_SET_M_TCLKTRAILCTL(x) REG_PUT(x, 7, 0)
+
+#define PHYTIMING2_SET_M_THSPRPRCTL(x) REG_PUT(x, 23, 16)
+#define PHYTIMING2_SET_M_THSZEROCTL(x) REG_PUT(x, 15, 8)
+#define PHYTIMING2_SET_M_THSTRAILCTL(x) REG_PUT(x, 7, 0)
+
+#define dsim_read(dsim, reg) readl(dsim->base + reg)
+#define dsim_write(dsim, val, reg) writel(val, dsim->base + reg)
+
+/* fixed phy ref clk rate */
+#define PHY_REF_CLK 27000000
+
+#define MAX_MAIN_HRESOL 2047
+#define MAX_MAIN_VRESOL 2047
+#define MAX_SUB_HRESOL 1024
+#define MAX_SUB_VRESOL 1024
+
+/* in KHZ */
+#define MAX_ESC_CLK_FREQ 20000
+
+/* dsim all irqs index */
+#define PLLSTABLE 1
+#define SWRSTRELEASE 2
+#define SFRPLFIFOEMPTY 3
+#define SFRPHFIFOEMPTY 4
+#define SYNCOVERRIDE 5
+#define BUSTURNOVER 6
+#define FRAMEDONE 7
+#define LPDRTOUT 8
+#define TATOUT 9
+#define RXDATDONE 10
+#define RXTE 11
+#define RXACK 12
+#define ERRRXECC 13
+#define ERRRXCRC 14
+#define ERRESC3 15
+#define ERRESC2 16
+#define ERRESC1 17
+#define ERRESC0 18
+#define ERRSYNC3 19
+#define ERRSYNC2 20
+#define ERRSYNC1 21
+#define ERRSYNC0 22
+#define ERRCONTROL3 23
+#define ERRCONTROL2 24
+#define ERRCONTROL1 25
+#define ERRCONTROL0 26
+
+/* Dispmix Control & GPR Registers */
+#define DISPLAY_MIX_SFT_RSTN_CSR 0x00
+ #define MIPI_DSI_I_PRESETn_SFT_EN BIT(5)
+#define DISPLAY_MIX_CLK_EN_CSR 0x04
+ #define MIPI_DSI_PCLK_SFT_EN BIT(8)
+ #define MIPI_DSI_CLKREF_SFT_EN BIT(9)
+#define GPR_MIPI_RESET_DIV 0x08
+ /* Clock & Data lanes reset: Active Low */
+ #define GPR_MIPI_S_RESETN BIT(16)
+ #define GPR_MIPI_M_RESETN BIT(17)
+
+#define DIV_ROUND_UP_ULL(ll,d) \
+ ({ unsigned long long _tmp = (ll)+(d)-1; do_div(_tmp, d); _tmp; })
+
+#define PS2KHZ(ps) (1000000000UL / (ps))
+
+/* DSIM PLL configuration from spec:
+ *
+ * Fout(DDR) = (M * Fin) / (P * 2^S), so Fout / Fin = M / (P * 2^S)
+ * Fin_pll = Fin / P (6 ~ 12 MHz)
+ * S: [2:0], M: [12:3], P: [18:13], so
+ * TODO: 'S' is in [0 ~ 3], 'M' is in, 'P' is in [1 ~ 33]
+ *
+ */
+
+struct sec_mipi_dsim {
+ void __iomem *base;
+ void __iomem *disp_mix_gpr_base;
+
+ /* kHz clocks */
+ uint64_t pix_clk;
+ uint64_t bit_clk;
+
+ unsigned int lanes;
+ unsigned int channel; /* virtual channel */
+ enum mipi_dsi_pixel_format format;
+ unsigned long mode_flags;
+ unsigned int pms;
+ unsigned int p;
+ unsigned int m;
+ unsigned int s;
+ struct fb_videomode vmode;
+
+ const struct sec_mipi_dsim_plat_data *pdata;
+
+ struct mipi_dsi_client_dev *dsi_panel_dev;
+ struct mipi_dsi_client_driver *dsi_panel_drv;
+};
+
+static void disp_mix_dsim_soft_reset_release(struct sec_mipi_dsim *dsim, bool release)
+{
+ if (release)
+ /* release dsi blk reset */
+ setbits_le32(dsim->disp_mix_gpr_base + DISPLAY_MIX_SFT_RSTN_CSR, MIPI_DSI_I_PRESETn_SFT_EN);
+
+ else
+ clrbits_le32(dsim->disp_mix_gpr_base + DISPLAY_MIX_SFT_RSTN_CSR, MIPI_DSI_I_PRESETn_SFT_EN);
+}
+
+static void disp_mix_dsim_clks_enable(struct sec_mipi_dsim *dsim, bool enable)
+{
+ if (enable)
+ setbits_le32(dsim->disp_mix_gpr_base + DISPLAY_MIX_CLK_EN_CSR, MIPI_DSI_PCLK_SFT_EN | MIPI_DSI_CLKREF_SFT_EN);
+ else
+ clrbits_le32(dsim->disp_mix_gpr_base + DISPLAY_MIX_CLK_EN_CSR, MIPI_DSI_PCLK_SFT_EN | MIPI_DSI_CLKREF_SFT_EN);
+}
+
+static void disp_mix_dsim_lanes_reset(struct sec_mipi_dsim *dsim, bool reset)
+{
+ if (!reset)
+ /* release lanes reset */
+ setbits_le32(dsim->disp_mix_gpr_base + GPR_MIPI_RESET_DIV, GPR_MIPI_S_RESETN | GPR_MIPI_M_RESETN);
+ else
+ /* reset lanes */
+ clrbits_le32(dsim->disp_mix_gpr_base + GPR_MIPI_RESET_DIV, GPR_MIPI_S_RESETN | GPR_MIPI_M_RESETN);
+}
+
+static void sec_mipi_dsim_wr_tx_header(struct sec_mipi_dsim *dsim,
+ u8 di, u8 data0, u8 data1)
+{
+ unsigned int reg;
+
+ reg = (data1 << 16) | (data0 << 8) | ((di & 0x3f) << 0);
+
+ dsim_write(dsim, reg, DSIM_PKTHDR);
+}
+
+static void sec_mipi_dsim_wr_tx_data(struct sec_mipi_dsim *dsim,
+ unsigned int tx_data)
+{
+ dsim_write(dsim, tx_data, DSIM_PAYLOAD);
+}
+
+static void sec_mipi_dsim_long_data_wr(struct sec_mipi_dsim *dsim,
+ const unsigned char *data0, unsigned int data_size)
+{
+ unsigned int data_cnt = 0, payload = 0;
+
+ /* in case that data count is more then 4 */
+ for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) {
+ /*
+ * after sending 4bytes per one time,
+ * send remainder data less then 4.
+ */
+ if ((data_size - data_cnt) < 4) {
+ if ((data_size - data_cnt) == 3) {
+ payload = data0[data_cnt] |
+ data0[data_cnt + 1] << 8 |
+ data0[data_cnt + 2] << 16;
+ debug("count = 3 payload = %x, %x %x %x\n",
+ payload, data0[data_cnt],
+ data0[data_cnt + 1],
+ data0[data_cnt + 2]);
+ } else if ((data_size - data_cnt) == 2) {
+ payload = data0[data_cnt] |
+ data0[data_cnt + 1] << 8;
+ debug("count = 2 payload = %x, %x %x\n", payload,
+ data0[data_cnt],
+ data0[data_cnt + 1]);
+ } else if ((data_size - data_cnt) == 1) {
+ payload = data0[data_cnt];
+ }
+
+ sec_mipi_dsim_wr_tx_data(dsim, payload);
+ /* send 4bytes per one time. */
+ } else {
+ payload = data0[data_cnt] |
+ data0[data_cnt + 1] << 8 |
+ data0[data_cnt + 2] << 16 |
+ data0[data_cnt + 3] << 24;
+
+ debug("count = 4 payload = %x, %x %x %x %x\n",
+ payload, *(u8 *)(data0 + data_cnt),
+ data0[data_cnt + 1],
+ data0[data_cnt + 2],
+ data0[data_cnt + 3]);
+
+ sec_mipi_dsim_wr_tx_data(dsim, payload);
+ }
+ }
+}
+
+static int sec_mipi_dsim_wait_for_pkt_done(struct sec_mipi_dsim *dsim, unsigned long timeout)
+{
+ uint32_t intsrc;
+
+ do {
+ intsrc = dsim_read(dsim, DSIM_INTSRC);
+ if (intsrc & INTSRC_SFRPLFIFOEMPTY) {
+ dsim_write(dsim, INTSRC_SFRPLFIFOEMPTY, DSIM_INTSRC);
+ return 0;
+ }
+
+ udelay(1);
+ } while (--timeout);
+
+ return -ETIMEDOUT;
+}
+
+static int sec_mipi_dsim_pkt_write(struct sec_mipi_dsim *dsim,
+ u8 data_type, const u8 *buf, int len)
+{
+ int ret = 0;
+ const unsigned char *data = (const unsigned char*)buf;
+
+ if (len == 0)
+ /* handle generic short write command */
+ sec_mipi_dsim_wr_tx_header(dsim, data_type, data[0], data[1]);
+ else {
+ /* handle generic long write command */
+ sec_mipi_dsim_long_data_wr(dsim, data, len);
+ sec_mipi_dsim_wr_tx_header(dsim, data_type, len & 0xff, (len & 0xff00) >> 8);
+
+ ret = sec_mipi_dsim_wait_for_pkt_done(dsim, MIPI_FIFO_TIMEOUT);
+ if (ret) {
+ printf("wait tx done timeout!\n");
+ return -ETIMEDOUT;
+ }
+ }
+ mdelay(10);
+
+ return 0;
+}
+
+static int sec_mipi_dsim_wait_pll_stable(struct sec_mipi_dsim *dsim)
+{
+ uint32_t status;
+ ulong start;
+
+ start = get_timer(0); /* Get current timestamp */
+
+ do {
+ status = dsim_read(dsim, DSIM_STATUS);
+ if (status & STATUS_PLLSTABLE)
+ return 0;
+ } while (get_timer(0) < (start + 100)); /* Wait 100ms */
+
+ return -ETIMEDOUT;
+}
+
+static int sec_mipi_dsim_config_pll(struct sec_mipi_dsim *dsim)
+{
+ int ret;
+ uint32_t pllctrl = 0, status, data_lanes_en, stop;
+
+ dsim_write(dsim, 0x8000, DSIM_PLLTMR);
+
+ /* TODO: config dp/dn swap if requires */
+
+ pllctrl |= PLLCTRL_SET_PMS(dsim->pms) | PLLCTRL_PLLEN;
+ dsim_write(dsim, pllctrl, DSIM_PLLCTRL);
+
+ ret = sec_mipi_dsim_wait_pll_stable(dsim);
+ if (ret) {
+ printf("wait for pll stable time out\n");
+ return ret;
+ }
+
+ /* wait for clk & data lanes to go to stop state */
+ mdelay(1);
+
+ data_lanes_en = (0x1 << dsim->lanes) - 1;
+ status = dsim_read(dsim, DSIM_STATUS);
+ if (!(status & STATUS_STOPSTATECLK)) {
+ printf("clock is not in stop state\n");
+ return -EBUSY;
+ }
+
+ stop = STATUS_GET_STOPSTATEDAT(status);
+ if ((stop & data_lanes_en) != data_lanes_en) {
+ printf("one or more data lanes is not in stop state\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void sec_mipi_dsim_set_main_mode(struct sec_mipi_dsim *dsim)
+{
+ uint32_t bpp, hfp_wc, hbp_wc, hsa_wc;
+ uint32_t mdresol = 0, mvporch = 0, mhporch = 0, msync = 0;
+ struct fb_videomode *vmode = &dsim->vmode;
+
+ mdresol |= MDRESOL_SET_MAINVRESOL(vmode->yres) |
+ MDRESOL_SET_MAINHRESOL(vmode->xres);
+ dsim_write(dsim, mdresol, DSIM_MDRESOL);
+
+ mvporch |= MVPORCH_SET_MAINVBP(vmode->upper_margin) |
+ MVPORCH_SET_STABLEVFP(vmode->lower_margin) |
+ MVPORCH_SET_CMDALLOW(0x0);
+ dsim_write(dsim, mvporch, DSIM_MVPORCH);
+
+ bpp = mipi_dsi_pixel_format_to_bpp(dsim->format);
+
+ /* calculate hfp & hbp word counts */
+ if (dsim->dsi_panel_drv) {
+ /* Panel driver is registered, will work with panel */
+ hfp_wc = vmode->right_margin * (bpp >> 3);
+ hbp_wc = vmode->left_margin * (bpp >> 3);
+ } else {
+ hfp_wc = vmode->right_margin * (bpp >> 3) / dsim->lanes - 6;
+ hbp_wc = vmode->left_margin * (bpp >> 3) / dsim->lanes - 6;
+ }
+
+ mhporch |= MHPORCH_SET_MAINHFP(hfp_wc) |
+ MHPORCH_SET_MAINHBP(hbp_wc);
+
+ dsim_write(dsim, mhporch, DSIM_MHPORCH);
+
+ /* calculate hsa word counts */
+ if (dsim->dsi_panel_drv) {
+ hsa_wc = vmode->hsync_len * (bpp >> 3);
+ } else {
+ hsa_wc = vmode->hsync_len * (bpp >> 3) / dsim->lanes - 6;
+ }
+
+ msync |= MSYNC_SET_MAINVSA(vmode->vsync_len) |
+ MSYNC_SET_MAINHSA(hsa_wc);
+
+ debug("hfp_wc %u hbp_wc %u hsa_wc %u\n", hfp_wc, hbp_wc, hsa_wc);
+
+ dsim_write(dsim, msync, DSIM_MSYNC);
+}
+
+static void sec_mipi_dsim_config_dpi(struct sec_mipi_dsim *dsim)
+{
+ uint32_t config = 0, rgb_status = 0, data_lanes_en;
+
+ if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO)
+ rgb_status &= ~RGB_STATUS_CMDMODE_INSEL;
+ else
+ rgb_status |= RGB_STATUS_CMDMODE_INSEL;
+
+ dsim_write(dsim, rgb_status, DSIM_RGB_STATUS);
+
+ if (dsim->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
+ config |= CONFIG_CLKLANE_STOP_START;
+
+ if (dsim->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)
+ config |= CONFIG_MFLUSH_VS;
+
+ /* disable EoT packets in HS mode */
+ if (dsim->mode_flags & MIPI_DSI_MODE_EOT_PACKET)
+ config |= CONFIG_EOT_R03;
+
+ if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) {
+ config |= CONFIG_VIDEOMODE;
+
+ if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ config |= CONFIG_BURSTMODE;
+
+ else if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+ config |= CONFIG_SYNCINFORM;
+
+ if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
+ config |= CONFIG_AUTOMODE;
+
+ if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
+ config |= CONFIG_HSEDISABLEMODE;
+
+ if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HFP)
+ config |= CONFIG_HFPDISABLEMODE;
+
+ if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HBP)
+ config |= CONFIG_HBPDISABLEMODE;
+
+ if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSA)
+ config |= CONFIG_HSADISABLEMODE;
+ }
+
+ config |= CONFIG_SET_MAINVC(dsim->channel);
+
+ if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) {
+ switch (dsim->format) {
+ case MIPI_DSI_FMT_RGB565:
+ config |= CONFIG_SET_MAINPIXFORMAT(0x4);
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ config |= CONFIG_SET_MAINPIXFORMAT(0x5);
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ config |= CONFIG_SET_MAINPIXFORMAT(0x6);
+ break;
+ case MIPI_DSI_FMT_RGB888:
+ config |= CONFIG_SET_MAINPIXFORMAT(0x7);
+ break;
+ default:
+ config |= CONFIG_SET_MAINPIXFORMAT(0x7);
+ break;
+ }
+ }
+
+ /* config data lanes number and enable lanes */
+ data_lanes_en = (0x1 << dsim->lanes) - 1;
+ config |= CONFIG_SET_NUMOFDATLANE(dsim->lanes - 1);
+ config |= CONFIG_SET_LANEEN(0x1 | data_lanes_en << 1);
+
+ dsim_write(dsim, config, DSIM_CONFIG);
+}
+
+static void sec_mipi_dsim_config_dphy(struct sec_mipi_dsim *dsim)
+{
+ uint32_t phytiming = 0, phytiming1 = 0, phytiming2 = 0, timeout = 0;
+
+ /* TODO: add a PHY timing table arranged by the pll Fout */
+
+ phytiming |= PHYTIMING_SET_M_TLPXCTL(6) |
+ PHYTIMING_SET_M_THSEXITCTL(11);
+ dsim_write(dsim, phytiming, DSIM_PHYTIMING);
+
+ phytiming1 |= PHYTIMING1_SET_M_TCLKPRPRCTL(7) |
+ PHYTIMING1_SET_M_TCLKZEROCTL(38) |
+ PHYTIMING1_SET_M_TCLKPOSTCTL(13) |
+ PHYTIMING1_SET_M_TCLKTRAILCTL(8);
+ dsim_write(dsim, phytiming1, DSIM_PHYTIMING1);
+
+ phytiming2 |= PHYTIMING2_SET_M_THSPRPRCTL(8) |
+ PHYTIMING2_SET_M_THSZEROCTL(13) |
+ PHYTIMING2_SET_M_THSTRAILCTL(11);
+ dsim_write(dsim, phytiming2, DSIM_PHYTIMING2);
+
+ timeout |= TIMEOUT_SET_BTAOUT(0xf) |
+ TIMEOUT_SET_LPDRTOUT(0xf);
+ dsim_write(dsim, 0xf000f, DSIM_TIMEOUT);
+}
+
+static void sec_mipi_dsim_config_clkctrl(struct sec_mipi_dsim *dsim)
+{
+ uint32_t clkctrl = 0, data_lanes_en;
+ uint64_t byte_clk, esc_prescaler;
+
+ clkctrl |= CLKCTRL_TXREQUESTHSCLK;
+
+ /* using 1.5Gbps PHY */
+ clkctrl |= CLKCTRL_DPHY_SEL_1P5G;
+
+ clkctrl |= CLKCTRL_ESCCLKEN;
+
+ clkctrl &= ~CLKCTRL_PLLBYPASS;
+
+ clkctrl |= CLKCTRL_BYTECLKSRC_DPHY_PLL;
+
+ clkctrl |= CLKCTRL_BYTECLKEN;
+
+ data_lanes_en = (0x1 << dsim->lanes) - 1;
+ clkctrl |= CLKCTRL_SET_LANEESCCLKEN(0x1 | data_lanes_en << 1);
+
+ /* calculate esc prescaler from byte clock:
+ * EscClk = ByteClk / EscPrescaler;
+ */
+ byte_clk = dsim->bit_clk >> 3;
+ esc_prescaler = DIV_ROUND_UP_ULL(byte_clk, MAX_ESC_CLK_FREQ);
+
+ clkctrl |= CLKCTRL_SET_ESCPRESCALER(esc_prescaler);
+
+ dsim_write(dsim, clkctrl, DSIM_CLKCTRL);
+}
+
+static void sec_mipi_dsim_set_standby(struct sec_mipi_dsim *dsim,
+ bool standby)
+{
+ uint32_t mdresol = 0;
+
+ mdresol = dsim_read(dsim, DSIM_MDRESOL);
+
+ if (standby)
+ mdresol |= MDRESOL_MAINSTANDBY;
+ else
+ mdresol &= ~MDRESOL_MAINSTANDBY;
+
+ dsim_write(dsim, mdresol, DSIM_MDRESOL);
+}
+
+static void sec_mipi_dsim_disable_clkctrl(struct sec_mipi_dsim *dsim)
+{
+ uint32_t clkctrl;
+
+ clkctrl = dsim_read(dsim, DSIM_CLKCTRL);
+
+ clkctrl &= ~CLKCTRL_TXREQUESTHSCLK;
+
+ clkctrl &= ~CLKCTRL_ESCCLKEN;
+
+ clkctrl &= ~CLKCTRL_BYTECLKEN;
+
+ dsim_write(dsim, clkctrl, DSIM_CLKCTRL);
+}
+
+static void sec_mipi_dsim_disable_pll(struct sec_mipi_dsim *dsim)
+{
+ uint32_t pllctrl;
+
+ pllctrl = dsim_read(dsim, DSIM_PLLCTRL);
+
+ pllctrl &= ~PLLCTRL_PLLEN;
+
+ dsim_write(dsim, pllctrl, DSIM_PLLCTRL);
+}
+
+/* For now, dsim only support one device attached */
+static int sec_mipi_dsim_bridge_attach(struct mipi_dsi_bridge_driver *bridge_driver,
+ struct mipi_dsi_client_dev *dsi_dev)
+{
+ struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private;
+
+ if (!dsi_dev->lanes || dsi_dev->lanes > dsim_host->pdata->max_data_lanes) {
+ printf("invalid data lanes number\n");
+ return -EINVAL;
+ }
+
+ if (!(dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO) ||
+ !((dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) ||
+ (dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))) {
+ printf("unsupported dsi mode\n");
+ return -EINVAL;
+ }
+
+ if (dsi_dev->format != MIPI_DSI_FMT_RGB888 &&
+ dsi_dev->format != MIPI_DSI_FMT_RGB565 &&
+ dsi_dev->format != MIPI_DSI_FMT_RGB666 &&
+ dsi_dev->format != MIPI_DSI_FMT_RGB666_PACKED) {
+ printf("unsupported pixel format: %#x\n", dsi_dev->format);
+ return -EINVAL;
+ }
+
+ if (!dsi_dev->name) {
+ printf("panel_device name is NULL.\n");
+ return -EFAULT;
+ }
+
+ if (dsim_host->dsi_panel_drv) {
+ if (strcmp(dsi_dev->name, dsim_host->dsi_panel_drv->name)) {
+ printf("The panel device name %s is not for LCD driver %s\n",
+ dsi_dev->name, dsim_host->dsi_panel_drv->name);
+ return -EFAULT;
+ }
+ }
+
+ dsim_host->dsi_panel_dev = dsi_dev;
+
+ dsim_host->lanes = dsi_dev->lanes;
+ dsim_host->channel = dsi_dev->channel;
+ dsim_host->format = dsi_dev->format;
+ dsim_host->mode_flags = dsi_dev->mode_flags;
+
+ return 0;
+}
+
+static int sec_mipi_dsim_bridge_enable(struct mipi_dsi_bridge_driver *bridge_driver)
+{
+ int ret;
+ struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private;
+
+ /* At this moment, the dsim bridge's preceding encoder has
+ * already been enabled. So the dsim can be configed here
+ */
+
+ /* config main display mode */
+ sec_mipi_dsim_set_main_mode(dsim_host);
+
+ /* config dsim dpi */
+ sec_mipi_dsim_config_dpi(dsim_host);
+
+ /* config dsim pll */
+ ret = sec_mipi_dsim_config_pll(dsim_host);
+ if (ret) {
+ printf("dsim pll config failed: %d\n", ret);
+ return -EPERM;
+ }
+
+ /* config dphy timings */
+ sec_mipi_dsim_config_dphy(dsim_host);
+
+ /* config esc clock, byte clock and etc */
+ sec_mipi_dsim_config_clkctrl(dsim_host);
+
+ /* enable data transfer of dsim */
+ sec_mipi_dsim_set_standby(dsim_host, true);
+
+ /* Call panel driver's setup */
+ if (dsim_host->dsi_panel_drv && dsim_host->dsi_panel_drv->dsi_client_setup) {
+ ret = dsim_host->dsi_panel_drv->dsi_client_setup(dsim_host->dsi_panel_dev);
+ if (ret < 0) {
+ printf("failed to init mipi lcd.\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int sec_mipi_dsim_bridge_disable(struct mipi_dsi_bridge_driver *bridge_driver)
+{
+ uint32_t intsrc;
+ struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private;
+
+ /* disable data transfer of dsim */
+ sec_mipi_dsim_set_standby(dsim_host, false);
+
+ /* disable esc clock & byte clock */
+ sec_mipi_dsim_disable_clkctrl(dsim_host);
+
+ /* disable dsim pll */
+ sec_mipi_dsim_disable_pll(dsim_host);
+
+ /* Clear all intsrc */
+ intsrc = dsim_read(dsim_host, DSIM_INTSRC);
+ dsim_write(dsim_host, intsrc, DSIM_INTSRC);
+
+ return 0;
+}
+
+static int sec_mipi_dsim_bridge_mode_set(struct mipi_dsi_bridge_driver *bridge_driver,
+ struct fb_videomode *fbmode)
+{
+ int bpp;
+ uint64_t pix_clk, bit_clk;
+ struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private;
+
+ dsim_host->vmode = *fbmode;
+
+ bpp = mipi_dsi_pixel_format_to_bpp(dsim_host->format);
+ if (bpp < 0)
+ return -EINVAL;
+
+ pix_clk = PS2KHZ(fbmode->pixclock) * 1000;
+ bit_clk = DIV_ROUND_UP_ULL(pix_clk * bpp, dsim_host->lanes);
+
+ if (bit_clk > dsim_host->pdata->max_data_rate) {
+ printf("request bit clk freq exceeds lane's maximum value\n");
+ return -EINVAL;
+ }
+
+ dsim_host->pix_clk = DIV_ROUND_UP_ULL(pix_clk, 1000);
+ dsim_host->bit_clk = DIV_ROUND_UP_ULL(bit_clk, 1000);
+
+ if (dsim_host->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
+ /* TODO: add PMS calculate and check
+ * Only support '1080p@60Hz' for now,
+ * add other modes support later
+ */
+ dsim_host->pms = 0x4210;
+ }
+
+ debug("%s: bitclk %llu pixclk %llu\n", __func__, dsim_host->bit_clk, dsim_host->pix_clk);
+
+ return 0;
+}
+
+/* Add a LCD panel driver, will search the panel device to bind with them */
+int sec_mipi_dsim_bridge_add_client_driver(struct mipi_dsi_bridge_driver *bridge_driver,
+ struct mipi_dsi_client_driver *panel_drv)
+{
+ struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private;
+
+ if (!panel_drv) {
+ printf("mipi_dsi_northwest_panel_driver is NULL.\n");
+ return -EFAULT;
+ }
+
+ if (!panel_drv->name) {
+ printf("mipi_dsi_northwest_panel_driver name is NULL.\n");
+ return -EFAULT;
+ }
+
+ if (dsim_host->dsi_panel_dev) {
+ if (strcmp(panel_drv->name, dsim_host->dsi_panel_dev->name)) {
+ printf("The panel driver name %s is not for LCD device %s\n",
+ panel_drv->name, dsim_host->dsi_panel_dev->name);
+ return -EFAULT;
+ }
+ }
+
+ dsim_host->dsi_panel_drv = panel_drv;
+
+ return 0;
+}
+
+static int sec_mipi_dsim_bridge_pkt_write(struct mipi_dsi_bridge_driver *bridge_driver,
+ u8 data_type, const u8 *buf, int len)
+{
+ struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private;
+
+#ifdef DEBUG
+ int i = 0;
+ printf("sec_mipi_dsim_bridge_pkt_write, data_type %u, len %d buf: \n", data_type, len);
+
+ if (len == 0)
+ len = 2;
+
+ for (i; i < len; i++) {
+ printf("0x%.2x ", buf[i]);
+ }
+ printf("\n");
+#endif
+
+ return sec_mipi_dsim_pkt_write(dsim_host, data_type, buf, len);
+}
+
+struct mipi_dsi_bridge_driver imx_sec_dsim_driver = {
+ .attach = sec_mipi_dsim_bridge_attach,
+ .enable = sec_mipi_dsim_bridge_enable,
+ .disable = sec_mipi_dsim_bridge_disable,
+ .mode_set = sec_mipi_dsim_bridge_mode_set,
+ .pkt_write = sec_mipi_dsim_bridge_pkt_write,
+ .add_client_driver = sec_mipi_dsim_bridge_add_client_driver,
+ .name = DRIVER_NAME,
+};
+
+int sec_mipi_dsim_setup(const struct sec_mipi_dsim_plat_data *plat_data)
+{
+ struct sec_mipi_dsim *dsim_host;
+
+ if (!plat_data) {
+ printf("Invalid platform data \n");
+ return -EINVAL;
+ }
+
+ dsim_host = (struct sec_mipi_dsim *)malloc(sizeof(struct sec_mipi_dsim));
+ if (!dsim_host) {
+ printf("failed to allocate sec_mipi_dsim object.\n");
+ return -ENOMEM;
+ }
+
+ dsim_host->base = (void __iomem *)plat_data->reg_base;
+ dsim_host->disp_mix_gpr_base = (void __iomem *)plat_data->gpr_base;
+ dsim_host->pdata = plat_data;
+ dsim_host->dsi_panel_drv = NULL;
+ dsim_host->dsi_panel_dev = NULL;
+
+ /* Pull dsim out of reset */
+ disp_mix_dsim_soft_reset_release(dsim_host, true);
+ disp_mix_dsim_clks_enable(dsim_host, true);
+ disp_mix_dsim_lanes_reset(dsim_host, false);
+
+ imx_sec_dsim_driver.driver_private = dsim_host;
+ return imx_mipi_dsi_bridge_register_driver(&imx_sec_dsim_driver);
+}
diff --git a/include/sec_mipi_dsim.h b/include/sec_mipi_dsim.h
new file mode 100644
index 0000000000..7bce9707f2
--- /dev/null
+++ b/include/sec_mipi_dsim.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __SEC_MIPI_DSIM_H__
+#define __SEC_MIPI_DSIM_H__
+
+struct sec_mipi_dsim_plat_data {
+ uint32_t version;
+ uint32_t max_data_lanes;
+ uint64_t max_data_rate;
+ ulong reg_base;
+ ulong gpr_base;
+};
+
+int sec_mipi_dsim_setup(const struct sec_mipi_dsim_plat_data *plat_data);
+
+#endif