summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYe Li <ye.li@nxp.com>2017-02-15 16:34:50 +0800
committerYe Li <ye.li@nxp.com>2017-04-05 17:24:34 +0800
commit1e984bba8cd961daa4c5bf994a6a90a72cc2f114 (patch)
tree0d0696a0b5857b707f22ca9160db85fd995b572d
parentb4e01a67a0740c524e7522da7ace0488f86261db (diff)
downloadu-boot-imx-1e984bba8cd961daa4c5bf994a6a90a72cc2f114.zip
u-boot-imx-1e984bba8cd961daa4c5bf994a6a90a72cc2f114.tar.gz
u-boot-imx-1e984bba8cd961daa4c5bf994a6a90a72cc2f114.tar.bz2
MLK-13929-1 video: Add MIPI DSI host controller driver for i.MX7ULP
Add the host driver base from kernel for MIPI DSI controller on i.MX7ULP. The controller provides a DPI-2 interface for LCDIF video stream, and a APB interface for packet transmission. The driver provides APIs to register a MIPI panel device and its driver. The panel driver can use the write packet function provided by the host driver to send control packets to panel device via APB interface. MIPI DSI has its PHY and dedicated PLL. The driver will setup them when enabling the DSI host. Signed-off-by: Ye Li <ye.li@nxp.com> (cherry picked from commit e02115dd1c5d36ec06eabcb5a0b8e09aaf0f29a0)
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/mipi_dsi_northwest.c584
-rw-r--r--drivers/video/mipi_dsi_northwest_regs.h142
-rw-r--r--include/mipi_dsi_northwest.h88
-rw-r--r--scripts/config_whitelist.txt1
5 files changed, 816 insertions, 0 deletions
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 5875f21..3e6664c 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -67,5 +67,6 @@ obj-$(CONFIG_VIDEO_VADC) += mxc_vadc.o
obj-$(CONFIG_VIDEO_CSI) += mxc_csi.o
obj-$(CONFIG_VIDEO_PXP) += mxc_pxp.o
obj-$(CONFIG_VIDEO_GIS) += mxc_gis.o
+obj-$(CONFIG_MXC_MIPI_DSI_NORTHWEST) += mipi_dsi_northwest.o
obj-y += bridge/
diff --git a/drivers/video/mipi_dsi_northwest.c b/drivers/video/mipi_dsi_northwest.c
new file mode 100644
index 0000000..30c8e28
--- /dev/null
+++ b/drivers/video/mipi_dsi_northwest.c
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2017 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <linux/string.h>
+
+#include "mipi_dsi_northwest_regs.h"
+#include <mipi_dsi_northwest.h>
+#include <mipi_display.h>
+
+#define MIPI_LCD_SLEEP_MODE_DELAY (120)
+#define MIPI_FIFO_TIMEOUT 250000 /* 250ms */
+
+enum mipi_dsi_mode {
+ DSI_COMMAND_MODE,
+ DSI_VIDEO_MODE
+};
+
+#define DSI_LP_MODE 0
+#define DSI_HS_MODE 1
+
+enum mipi_dsi_payload {
+ DSI_PAYLOAD_CMD,
+ DSI_PAYLOAD_VIDEO,
+};
+
+/**
+ * board_mipi_panel_reset - give a reset cycle for mipi dsi panel
+ *
+ * Target board specific, like use gpio to reset the dsi panel
+ * Machine board file overrides board_mipi_panel_reset
+ *
+ * Return: 0 Success
+ */
+int __weak board_mipi_panel_reset(void)
+{
+ return 0;
+}
+
+/**
+ * board_mipi_panel_shutdown - Shut down the mipi dsi panel
+ *
+ * Target board specific, like use gpio to shut down the dsi panel
+ * Machine board file overrides board_mipi_panel_shutdown
+ *
+ * Return: 0 Success
+ */
+int __weak board_mipi_panel_shutdown(void)
+{
+ return 0;
+}
+
+static void mipi_dsi_set_mode(struct mipi_dsi_northwest_info *mipi_dsi,
+ uint8_t mode);
+static int mipi_dsi_dcs_cmd(struct mipi_dsi_northwest_info *mipi_dsi,
+ u8 cmd, const u32 *param, int num);
+
+static void mipi_dsi_set_mode(struct mipi_dsi_northwest_info *mipi_dsi,
+ uint8_t mode)
+{
+ switch (mode) {
+ case DSI_LP_MODE:
+ writel(0x1, mipi_dsi->mmio_base + HOST_CFG_NONCONTINUOUS_CLK);
+ break;
+ case DSI_HS_MODE:
+ writel(0x0, mipi_dsi->mmio_base + HOST_CFG_NONCONTINUOUS_CLK);
+ break;
+ default:
+ printf("invalid dsi mode\n");
+ return;
+ }
+
+ mdelay(1);
+}
+
+static int mipi_dsi_dphy_init(struct mipi_dsi_northwest_info *mipi_dsi)
+{
+ uint32_t time_out = 100;
+ uint32_t CN, CM, CO;
+ uint32_t lock;
+
+ setbits_le32(mipi_dsi->sim_base + SIM_SOPT1, MIPI_ISO_DISABLE);
+
+ /* According to the RM, the dpi_pclk and clk_byte frequencies are related by the following formula:
+ * clk_byte_freq >= dpi_pclk_freq * DPI_pixel_size / ( 8 * (cfg_num_lanes + 1))
+ */
+
+ /* PLL out clock = refclk * CM / (CN * CO)
+ * refclock = 24MHz
+ * pll vco = 24 * 40 / (3 * 1) = 320MHz
+ */
+ CN = 0x10; /* 3 */
+ CM = 0xc8; /* 40 */
+ CO = 0x0; /* 1 */
+
+ writel(CN, mipi_dsi->mmio_base + DPHY_CN);
+ writel(CM, mipi_dsi->mmio_base + DPHY_CM);
+ writel(CO, mipi_dsi->mmio_base + DPHY_CO);
+
+ writel(0x25, mipi_dsi->mmio_base + DPHY_TST);
+ writel(0x0, mipi_dsi->mmio_base + DPHY_PD_PLL);
+
+ while (!(lock = readl(mipi_dsi->mmio_base + DPHY_LOCK))) {
+ udelay(10);
+ time_out--;
+ if (time_out == 0) {
+ printf("cannot get the dphy lock = 0x%x\n", lock);
+ return -EINVAL;
+ }
+ }
+ debug("%s: dphy lock = 0x%x\n", __func__, lock);
+
+ writel(0x0, mipi_dsi->mmio_base + DPHY_LOCK_BYP);
+ writel(0x1, mipi_dsi->mmio_base + DPHY_RTERM_SEL);
+ writel(0x0, mipi_dsi->mmio_base + DPHY_AUTO_PD_EN);
+ writel(0x1, mipi_dsi->mmio_base + DPHY_RXLPRP);
+ writel(0x1, mipi_dsi->mmio_base + DPHY_RXCDRP);
+ writel(0x0, mipi_dsi->mmio_base + DPHY_M_PRG_HS_PREPARE);
+ writel(0x0, mipi_dsi->mmio_base + DPHY_MC_PRG_HS_PREPARE);
+ writel(0x9, mipi_dsi->mmio_base + DPHY_M_PRG_HS_ZERO);
+ writel(0x20, mipi_dsi->mmio_base + DPHY_MC_PRG_HS_ZERO);
+ writel(0x5, mipi_dsi->mmio_base + DPHY_M_PRG_HS_TRAIL);
+ writel(0x5, mipi_dsi->mmio_base + DPHY_MC_PRG_HS_TRAIL);
+ writel(0x0, mipi_dsi->mmio_base + DPHY_PD_DPHY);
+
+ setbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_PLL_EN);
+ return 0;
+}
+
+static int mipi_dsi_host_init(struct mipi_dsi_northwest_info *mipi_dsi)
+{
+ uint32_t lane_num;
+
+ switch (mipi_dsi->dsi_panel_dev->data_lane_num) {
+ case 1:
+ lane_num = 0x0;
+ break;
+ case 2:
+ lane_num = 0x1;
+ break;
+ default:
+ /* Invalid lane num */
+ return -EINVAL;
+ }
+
+ writel(lane_num, mipi_dsi->mmio_base + HOST_CFG_NUM_LANES);
+ writel(0x1, mipi_dsi->mmio_base + HOST_CFG_NONCONTINUOUS_CLK);
+ writel(0x1, mipi_dsi->mmio_base + HOST_CFG_T_PRE);
+ writel(52, mipi_dsi->mmio_base + HOST_CFG_T_POST);
+ writel(13, mipi_dsi->mmio_base + HOST_CFG_TX_GAP);
+ writel(0x1, mipi_dsi->mmio_base + HOST_CFG_AUTOINSERT_EOTP);
+ writel(0x0, mipi_dsi->mmio_base + HOST_CFG_EXTRA_CMDS_AFTER_EOTP);
+ writel(0x0, mipi_dsi->mmio_base + HOST_CFG_HTX_TO_COUNT);
+ writel(0x0, mipi_dsi->mmio_base + HOST_CFG_LRX_H_TO_COUNT);
+ writel(0x0, mipi_dsi->mmio_base + HOST_CFG_BTA_H_TO_COUNT);
+ writel(0x3A98, mipi_dsi->mmio_base + HOST_CFG_TWAKEUP);
+
+ return 0;
+}
+
+static int mipi_dsi_dpi_init(struct mipi_dsi_northwest_info *mipi_dsi)
+{
+ uint32_t bpp, color_coding, pixel_fmt;
+ struct fb_videomode *mode = &(mipi_dsi->dsi_panel_dev->mode);
+
+ bpp = mipi_dsi->dsi_panel_dev->bpp;
+
+ writel(mode->xres, mipi_dsi->mmio_base + DPI_PIXEL_PAYLOAD_SIZE);
+ writel(mode->xres, mipi_dsi->mmio_base + DPI_PIXEL_FIFO_SEND_LEVEL);
+
+ switch (bpp) {
+ case 24:
+ color_coding = 5;
+ pixel_fmt = 3;
+ break;
+ case 16:
+ case 18:
+ default:
+ /* Not supported */
+ return -EINVAL;
+ }
+ writel(color_coding, mipi_dsi->mmio_base + DPI_INTERFACE_COLOR_CODING);
+ writel(pixel_fmt, mipi_dsi->mmio_base + DPI_PIXEL_FORMAT);
+ writel(0x0, mipi_dsi->mmio_base + DPI_VSYNC_POLARITY);
+ writel(0x0, mipi_dsi->mmio_base + DPI_HSYNC_POLARITY);
+ writel(0x2, mipi_dsi->mmio_base + DPI_VIDEO_MODE);
+
+ writel(mode->right_margin * (bpp >> 3), mipi_dsi->mmio_base + DPI_HFP);
+ writel(mode->left_margin * (bpp >> 3), mipi_dsi->mmio_base + DPI_HBP);
+ writel(mode->hsync_len * (bpp >> 3), mipi_dsi->mmio_base + DPI_HSA);
+ writel(0x0, mipi_dsi->mmio_base + DPI_ENABLE_MULT_PKTS);
+
+ writel(mode->upper_margin, mipi_dsi->mmio_base + DPI_VBP);
+ writel(mode->lower_margin, mipi_dsi->mmio_base + DPI_VFP);
+ writel(0x1, mipi_dsi->mmio_base + DPI_BLLP_MODE);
+ writel(0x0, mipi_dsi->mmio_base + DPI_USE_NULL_PKT_BLLP);
+
+ writel(mode->yres - 1, mipi_dsi->mmio_base + DPI_VACTIVE);
+
+ writel(0x0, mipi_dsi->mmio_base + DPI_VC);
+
+ return 0;
+}
+
+static void mipi_dsi_init_interrupt(struct mipi_dsi_northwest_info *mipi_dsi)
+{
+ /* disable all the irqs */
+ writel(0xffffffff, mipi_dsi->mmio_base + HOST_IRQ_MASK);
+ writel(0x7, mipi_dsi->mmio_base + HOST_IRQ_MASK2);
+}
+
+static int mipi_display_enter_sleep(struct mipi_dsi_northwest_info *mipi_dsi)
+{
+ int err;
+
+ err = mipi_dsi_dcs_cmd(mipi_dsi, MIPI_DCS_SET_DISPLAY_OFF,
+ NULL, 0);
+ if (err)
+ return -EINVAL;
+ mdelay(50);
+
+ err = mipi_dsi_dcs_cmd(mipi_dsi, MIPI_DCS_ENTER_SLEEP_MODE,
+ NULL, 0);
+ if (err)
+ printf("MIPI DSI DCS Command sleep in error!\n");
+
+ mdelay(MIPI_LCD_SLEEP_MODE_DELAY);
+
+ return err;
+}
+
+static int mipi_dsi_enable(struct mipi_dsi_northwest_info *mipi_dsi)
+{
+ int ret;
+
+ /* Assert resets */
+ /* escape domain */
+ clrbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_RST_ESC_N);
+
+ /* byte domain */
+ clrbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_RST_BYTE_N);
+
+ /* dpi domain */
+ clrbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_RST_DPI_N);
+
+ /* Enable mipi relevant clocks */
+ enable_mipi_dsi_clk(1);
+
+ ret = mipi_dsi_dphy_init(mipi_dsi);
+ if (ret < 0)
+ return ret;
+
+ ret = mipi_dsi_host_init(mipi_dsi);
+ if (ret < 0)
+ return ret;
+
+ ret = mipi_dsi_dpi_init(mipi_dsi);
+ if (ret < 0)
+ return ret;
+
+ /* Deassert resets */
+ /* escape domain */
+ setbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_RST_ESC_N);
+
+ /* byte domain */
+ setbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_RST_BYTE_N);
+
+ /* dpi domain */
+ setbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_RST_DPI_N);
+
+ /* display_en */
+ clrbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_SD);
+
+ /* normal cm */
+ clrbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_CM);
+ mdelay(20);
+
+ /* Reset mipi panel */
+ board_mipi_panel_reset();
+ mdelay(60);
+
+ /* Disable all interrupts, since we use polling */
+ mipi_dsi_init_interrupt(mipi_dsi);
+
+ /* Call panel driver's setup */
+ if (mipi_dsi->dsi_panel_drv->mipi_panel_setup) {
+ ret = mipi_dsi->dsi_panel_drv->mipi_panel_setup(mipi_dsi->dsi_panel_dev);
+ if (ret < 0) {
+ printf("failed to init mipi lcd.\n");
+ return ret;
+ }
+ }
+
+ /* Enter the HS mode for video stream */
+ mipi_dsi_set_mode(mipi_dsi, DSI_HS_MODE);
+
+ return 0;
+}
+
+static void mipi_dsi_wr_tx_header(struct mipi_dsi_northwest_info *mipi_dsi,
+ u8 di, u8 data0, u8 data1, u8 mode, u8 need_bta)
+{
+ uint32_t pkt_control = 0;
+ uint16_t word_count = 0;
+
+ word_count = data0 | (data1 << 8);
+ pkt_control = HOST_PKT_CONTROL_WC(word_count) |
+ HOST_PKT_CONTROL_VC(0) |
+ HOST_PKT_CONTROL_DT(di) |
+ HOST_PKT_CONTROL_HS_SEL(mode) |
+ HOST_PKT_CONTROL_BTA_TX(need_bta);
+
+ debug("pkt_control = %x\n", pkt_control);
+ writel(pkt_control, mipi_dsi->mmio_base + HOST_PKT_CONTROL);
+}
+
+static void mipi_dsi_wr_tx_data(struct mipi_dsi_northwest_info *mipi_dsi,
+ uint32_t tx_data)
+{
+ writel(tx_data, mipi_dsi->mmio_base + HOST_TX_PAYLOAD);
+}
+
+static void mipi_dsi_long_data_wr(struct mipi_dsi_northwest_info *mipi_dsi,
+ const uint8_t *data0, uint32_t data_size)
+{
+ uint32_t data_cnt = 0, payload = 0;
+
+ /* in case that data count is more than 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];
+ debug("count = 1 payload = %x, %x\n",
+ payload, data0[data_cnt]);
+ }
+
+ mipi_dsi_wr_tx_data(mipi_dsi, payload);
+ } 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]);
+
+ mipi_dsi_wr_tx_data(mipi_dsi, payload);
+ }
+ }
+}
+
+static int wait_for_pkt_done(struct mipi_dsi_northwest_info *mipi_dsi, unsigned long timeout)
+{
+ uint32_t irq_status;
+
+ do {
+ irq_status = readl(mipi_dsi->mmio_base + HOST_PKT_STATUS);
+ if (irq_status & HOST_IRQ_STATUS_TX_PKT_DONE)
+ return timeout;
+
+ udelay(1);
+ } while (--timeout);
+
+ return 0;
+}
+
+static int mipi_dsi_pkt_write(struct mipi_dsi_northwest_info *mipi_dsi,
+ u8 data_type, const u32 *buf, int len)
+{
+ int ret = 0;
+ const uint8_t *data = (const uint8_t *)buf;
+
+ debug("mipi_dsi_pkt_write data_type 0x%x, buf 0x%x, len %u\n", data_type, (u32)buf, len);
+
+ if (len == 0)
+ /* handle generic long write command */
+ mipi_dsi_wr_tx_header(mipi_dsi, data_type, data[0], data[1], DSI_LP_MODE, 0);
+ else {
+ /* handle generic long write command */
+ mipi_dsi_long_data_wr(mipi_dsi, data, len);
+ mipi_dsi_wr_tx_header(mipi_dsi, data_type, len & 0xff,
+ (len & 0xff00) >> 8, DSI_LP_MODE, 0);
+ }
+
+ /* send packet */
+ writel(0x1, mipi_dsi->mmio_base + HOST_SEND_PACKET);
+ ret = wait_for_pkt_done(mipi_dsi, MIPI_FIFO_TIMEOUT);
+
+ if (!ret) {
+ printf("wait tx done timeout!\n");
+ return -ETIMEDOUT;
+ }
+ mdelay(10);
+
+ return 0;
+}
+
+static int mipi_dsi_dcs_cmd(struct mipi_dsi_northwest_info *mipi_dsi,
+ u8 cmd, const u32 *param, int num)
+{
+ int err = 0;
+ u32 buf[DSI_CMD_BUF_MAXSIZE];
+
+ switch (cmd) {
+ case MIPI_DCS_EXIT_SLEEP_MODE:
+ case MIPI_DCS_ENTER_SLEEP_MODE:
+ case MIPI_DCS_SET_DISPLAY_ON:
+ case MIPI_DCS_SET_DISPLAY_OFF:
+ buf[0] = cmd;
+ buf[1] = 0x0;
+ err = mipi_dsi_pkt_write(mipi_dsi,
+ MIPI_DSI_DCS_SHORT_WRITE, buf, 0);
+ break;
+
+ default:
+ printf("MIPI DSI DCS Command:0x%x Not supported!\n", cmd);
+ break;
+ }
+
+ return err;
+}
+
+static void mipi_dsi_shutdown(struct mipi_dsi_northwest_info *mipi_dsi)
+{
+ mipi_display_enter_sleep(mipi_dsi);
+
+ writel(0x1, mipi_dsi->mmio_base + DPHY_PD_PLL);
+ writel(0x1, mipi_dsi->mmio_base + DPHY_PD_DPHY);
+
+ enable_mipi_dsi_clk(0);
+
+ /* Assert resets */
+ /* escape domain */
+ clrbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_RST_ESC_N);
+
+ /* byte domain */
+ clrbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_RST_BYTE_N);
+
+ /* dpi domain */
+ clrbits_le32(mipi_dsi->sim_base + SIM_SOPT1CFG, DSI_RST_DPI_N);
+}
+
+struct mipi_dsi_northwest_info *dsi_info = NULL;
+
+int mipi_dsi_northwest_setup(u32 base_addr, u32 sim_addr)
+{
+ if (dsi_info != NULL) {
+ printf("mipi_dsi_northwest has been initialized.\n");
+ return -EBUSY;
+ }
+
+ dsi_info = (struct mipi_dsi_northwest_info *)malloc(sizeof(struct mipi_dsi_northwest_info));
+ if (!dsi_info) {
+ printf("failed to allocate mipi_dsi_northwest_info object.\n");
+ return -ENOMEM;
+ }
+
+ dsi_info->mmio_base = base_addr;
+ dsi_info->sim_base = sim_addr;
+ dsi_info->mipi_dsi_pkt_write = &mipi_dsi_pkt_write;
+ dsi_info->dsi_panel_dev = NULL;
+ dsi_info->dsi_panel_drv = NULL;
+ dsi_info->enabled = 0;
+
+ return 0;
+}
+
+/* Register a LCD panel device */
+int mipi_dsi_northwest_register_panel_device(struct mipi_dsi_northwest_panel_device *panel_dev)
+{
+ if (!panel_dev) {
+ printf("mipi_dsi_northwest_panel_device is NULL.\n");
+ return -EFAULT;
+ }
+
+ if (!panel_dev->name) {
+ printf("mipi_dsi_northwest_panel_device name is NULL.\n");
+ return -EFAULT;
+ }
+
+ if (!dsi_info) {
+ printf("mipi_dsi_northwest is not initialized\n");
+ return -EFAULT;
+ }
+
+ if (dsi_info->dsi_panel_drv) {
+ if (strcmp(panel_dev->name, dsi_info->dsi_panel_drv->name)) {
+ printf("The panel device name %s is not for LCD driver %s\n",
+ panel_dev->name, dsi_info->dsi_panel_drv->name);
+ return -EFAULT;
+ }
+ }
+
+ dsi_info->dsi_panel_dev = panel_dev;
+ panel_dev->host = dsi_info;
+
+ return 0;
+}
+
+/* Register a LCD panel driver, will search the panel device to bind with them */
+int mipi_dsi_northwest_register_panel_driver(struct mipi_dsi_northwest_panel_driver *panel_drv)
+{
+ 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 (!dsi_info) {
+ printf("mipi_dsi_northwest is not initialized\n");
+ return -EFAULT;
+ }
+
+ if (dsi_info->dsi_panel_dev) {
+ if (strcmp(panel_drv->name, dsi_info->dsi_panel_dev->name)) {
+ printf("The panel driver name %s is not for LCD device %s\n",
+ panel_drv->name, dsi_info->dsi_panel_dev->name);
+ return -EFAULT;
+ }
+ }
+
+ dsi_info->dsi_panel_drv = panel_drv;
+
+ return 0;
+}
+
+/* Enable the mipi dsi display */
+int mipi_dsi_northwest_enable(void)
+{
+ if (!dsi_info->dsi_panel_dev || !dsi_info->dsi_panel_drv)
+ return -ENODEV;
+
+ mipi_dsi_enable(dsi_info);
+
+ dsi_info->enabled = 1;
+
+ return 0;
+}
+
+/* Disable and shutdown the mipi dsi display */
+int mipi_dsi_northwest_shutdown(void)
+{
+ if (!dsi_info->enabled)
+ return 0;
+
+ mipi_dsi_shutdown(dsi_info);
+ board_mipi_panel_shutdown();
+
+ dsi_info->enabled = 0;
+
+ return 0;
+}
diff --git a/drivers/video/mipi_dsi_northwest_regs.h b/drivers/video/mipi_dsi_northwest_regs.h
new file mode 100644
index 0000000..6493403
--- /dev/null
+++ b/drivers/video/mipi_dsi_northwest_regs.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2017 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+
+#ifndef __MIPI_DSI_NORTHWEST_REGS_H
+#define __MIPI_DSI_NORTHWEST_REGS_H
+
+/* ---------------------------- register offsets --------------------------- */
+
+/* sim */
+#define SIM_SOPT1 0x0
+#define MIPI_ISO_DISABLE 0x8
+
+#define SIM_SOPT1CFG 0x4
+#define DSI_RST_DPI_N 0x80000000
+#define DSI_RST_ESC_N 0x40000000
+#define DSI_RST_BYTE_N 0x20000000
+#define DSI_SD 0x200
+#define DSI_CM 0x100
+#define DSI_PLL_EN 0x80
+
+/* dphy */
+#define DPHY_PD_DPHY 0x300
+#define DPHY_M_PRG_HS_PREPARE 0x304
+#define DPHY_MC_PRG_HS_PREPARE 0x308
+#define DPHY_M_PRG_HS_ZERO 0x30c
+#define DPHY_MC_PRG_HS_ZERO 0x310
+#define DPHY_M_PRG_HS_TRAIL 0x314
+#define DPHY_MC_PRG_HS_TRAIL 0x318
+#define DPHY_PD_PLL 0x31c
+#define DPHY_TST 0x320
+#define DPHY_CN 0x324
+#define DPHY_CM 0x328
+#define DPHY_CO 0x32c
+#define DPHY_LOCK 0x330
+#define DPHY_LOCK_BYP 0x334
+#define DPHY_RTERM_SEL 0x338
+#define DPHY_AUTO_PD_EN 0x33c
+#define DPHY_RXLPRP 0x340
+#define DPHY_RXCDRP 0x344
+
+/* host */
+#define HOST_CFG_NUM_LANES 0x0
+#define HOST_CFG_NONCONTINUOUS_CLK 0x4
+#define HOST_CFG_T_PRE 0x8
+#define HOST_CFG_T_POST 0xc
+#define HOST_CFG_TX_GAP 0x10
+#define HOST_CFG_AUTOINSERT_EOTP 0x14
+#define HOST_CFG_EXTRA_CMDS_AFTER_EOTP 0x18
+#define HOST_CFG_HTX_TO_COUNT 0x1c
+#define HOST_CFG_LRX_H_TO_COUNT 0x20
+#define HOST_CFG_BTA_H_TO_COUNT 0x24
+#define HOST_CFG_TWAKEUP 0x28
+#define HOST_CFG_STATUS_OUT 0x2c
+#define HOST_RX_ERROR_STATUS 0x30
+
+/* dpi */
+#define DPI_PIXEL_PAYLOAD_SIZE 0x200
+#define DPI_PIXEL_FIFO_SEND_LEVEL 0x204
+#define DPI_INTERFACE_COLOR_CODING 0x208
+#define DPI_PIXEL_FORMAT 0x20c
+#define DPI_VSYNC_POLARITY 0x210
+#define DPI_HSYNC_POLARITY 0x214
+#define DPI_VIDEO_MODE 0x218
+#define DPI_HFP 0x21c
+#define DPI_HBP 0x220
+#define DPI_HSA 0x224
+#define DPI_ENABLE_MULT_PKTS 0x228
+#define DPI_VBP 0x22c
+#define DPI_VFP 0x230
+#define DPI_BLLP_MODE 0x234
+#define DPI_USE_NULL_PKT_BLLP 0x238
+#define DPI_VACTIVE 0x23c
+#define DPI_VC 0x240
+
+/* apb pkt */
+#define HOST_TX_PAYLOAD 0x280
+
+#define HOST_PKT_CONTROL 0x284
+#define HOST_PKT_CONTROL_WC(x) (((x) & 0xffff) << 0)
+#define HOST_PKT_CONTROL_VC(x) (((x) & 0x3) << 16)
+#define HOST_PKT_CONTROL_DT(x) (((x) & 0x3f) << 18)
+#define HOST_PKT_CONTROL_HS_SEL(x) (((x) & 0x1) << 24)
+#define HOST_PKT_CONTROL_BTA_TX(x) (((x) & 0x1) << 25)
+#define HOST_PKT_CONTROL_BTA_NO_TX(x) (((x) & 0x1) << 26)
+
+#define HOST_SEND_PACKET 0x288
+#define HOST_PKT_STATUS 0x28c
+#define HOST_PKT_FIFO_WR_LEVEL 0x290
+#define HOST_PKT_FIFO_RD_LEVEL 0x294
+#define HOST_PKT_RX_PAYLOAD 0x298
+
+#define HOST_PKT_RX_PKT_HEADER 0x29c
+#define HOST_PKT_RX_PKT_HEADER_WC(x) (((x) & 0xffff) << 0)
+#define HOST_PKT_RX_PKT_HEADER_DT(x) (((x) & 0x3f) << 16)
+#define HOST_PKT_RX_PKT_HEADER_VC(x) (((x) & 0x3) << 22)
+
+#define HOST_IRQ_STATUS 0x2a0
+#define HOST_IRQ_STATUS_SM_NOT_IDLE (1 << 0)
+#define HOST_IRQ_STATUS_TX_PKT_DONE (1 << 1)
+#define HOST_IRQ_STATUS_DPHY_DIRECTION (1 << 2)
+#define HOST_IRQ_STATUS_TX_FIFO_OVFLW (1 << 3)
+#define HOST_IRQ_STATUS_TX_FIFO_UDFLW (1 << 4)
+#define HOST_IRQ_STATUS_RX_FIFO_OVFLW (1 << 5)
+#define HOST_IRQ_STATUS_RX_FIFO_UDFLW (1 << 6)
+#define HOST_IRQ_STATUS_RX_PKT_HDR_RCVD (1 << 7)
+#define HOST_IRQ_STATUS_RX_PKT_PAYLOAD_DATA_RCVD (1 << 8)
+#define HOST_IRQ_STATUS_HOST_BTA_TIMEOUT (1 << 29)
+#define HOST_IRQ_STATUS_LP_RX_TIMEOUT (1 << 30)
+#define HOST_IRQ_STATUS_HS_TX_TIMEOUT (1 << 31)
+
+#define HOST_IRQ_STATUS2 0x2a4
+#define HOST_IRQ_STATUS2_SINGLE_BIT_ECC_ERR (1 << 0)
+#define HOST_IRQ_STATUS2_MULTI_BIT_ECC_ERR (1 << 1)
+#define HOST_IRQ_STATUS2_CRC_ERR (1 << 2)
+
+#define HOST_IRQ_MASK 0x2a8
+#define HOST_IRQ_MASK_SM_NOT_IDLE_MASK (1 << 0)
+#define HOST_IRQ_MASK_TX_PKT_DONE_MASK (1 << 1)
+#define HOST_IRQ_MASK_DPHY_DIRECTION_MASK (1 << 2)
+#define HOST_IRQ_MASK_TX_FIFO_OVFLW_MASK (1 << 3)
+#define HOST_IRQ_MASK_TX_FIFO_UDFLW_MASK (1 << 4)
+#define HOST_IRQ_MASK_RX_FIFO_OVFLW_MASK (1 << 5)
+#define HOST_IRQ_MASK_RX_FIFO_UDFLW_MASK (1 << 6)
+#define HOST_IRQ_MASK_RX_PKT_HDR_RCVD_MASK (1 << 7)
+#define HOST_IRQ_MASK_RX_PKT_PAYLOAD_DATA_RCVD_MASK (1 << 8)
+#define HOST_IRQ_MASK_HOST_BTA_TIMEOUT_MASK (1 << 29)
+#define HOST_IRQ_MASK_LP_RX_TIMEOUT_MASK (1 << 30)
+#define HOST_IRQ_MASK_HS_TX_TIMEOUT_MASK (1 << 31)
+
+#define HOST_IRQ_MASK2 0x2ac
+#define HOST_IRQ_MASK2_SINGLE_BIT_ECC_ERR_MASK (1 << 0)
+#define HOST_IRQ_MASK2_MULTI_BIT_ECC_ERR_MASK (1 << 1)
+#define HOST_IRQ_MASK2_CRC_ERR_MASK (1 << 2)
+
+/* ------------------------------------- end -------------------------------- */
+
+#endif
diff --git a/include/mipi_dsi_northwest.h b/include/mipi_dsi_northwest.h
new file mode 100644
index 0000000..8795ede
--- /dev/null
+++ b/include/mipi_dsi_northwest.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2017 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+
+#ifndef __MIPI_DSI_NORTHWEST_H
+#define __MIPI_DSI_NORTHWEST_H
+
+#include <linux/fb.h>
+
+#define DSI_CMD_BUF_MAXSIZE (128)
+
+/*
+ * device structure for mipi-dsi based lcd panel.
+ *
+ * @name: name of the device to use with this device,
+ * the name will be used for binding driver.
+ * @mode: video mode parameters for the panel.
+ * @bpp: bits per pixel. only 24 bits is supported.
+ * @virtual_ch_id: virtual channel id for this dsi device.
+ * @data_lane_num: the data lane number, max is 2.
+ * @host: pointer to the host driver instance, will be setup
+ * during register device.
+ */
+struct mipi_dsi_northwest_panel_device {
+ const char *name;
+ struct fb_videomode mode;
+ int bpp;
+ u32 virtual_ch_id;
+ u32 data_lane_num;
+
+ struct mipi_dsi_northwest_info *host;
+};
+
+
+/*
+ * driver structure for mipi-dsi based lcd panel.
+ *
+ * this structure should be registered by lcd panel driver.
+ * mipi-dsi driver seeks lcd panel registered through name field
+ * and calls these callback functions in appropriate time.
+ *
+ * @name: name of the driver to use with this device, or an
+ * alias for that name.
+ * @mipi_panel_setup: callback pointer for initializing lcd panel based on mipi
+ * dsi interface.
+ */
+struct mipi_dsi_northwest_panel_driver {
+ const char *name;
+
+ int (*mipi_panel_setup)(struct mipi_dsi_northwest_panel_device *panel_dev);
+};
+
+/*
+ * mipi-dsi northwest driver information structure, holds useful data for the driver.
+ */
+struct mipi_dsi_northwest_info {
+ u32 mmio_base;
+ u32 sim_base;
+ int enabled;
+ struct mipi_dsi_northwest_panel_device *dsi_panel_dev;
+ struct mipi_dsi_northwest_panel_driver *dsi_panel_drv;
+
+ int (*mipi_dsi_pkt_write)(struct mipi_dsi_northwest_info *mipi_dsi,
+ u8 data_type, const u32 *buf, int len);
+};
+
+/* Setup mipi dsi host driver instance, with base address and SIM address provided */
+int mipi_dsi_northwest_setup(u32 base_addr, u32 sim_addr);
+
+/* Create a LCD panel device, will search the panel driver to bind with them */
+int mipi_dsi_northwest_register_panel_device(struct mipi_dsi_northwest_panel_device *panel_dev);
+
+/* Register a LCD panel driver, will search the panel device to bind with them */
+int mipi_dsi_northwest_register_panel_driver(struct mipi_dsi_northwest_panel_driver *panel_drv);
+
+/* Enable the mipi dsi display */
+int mipi_dsi_northwest_enable(void);
+
+/* Disable and shutdown the mipi dsi display */
+int mipi_dsi_northwest_shutdown(void);
+
+void hx8363_init(void);
+
+#endif
diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt
index ff1d8ae..a0213c2 100644
--- a/scripts/config_whitelist.txt
+++ b/scripts/config_whitelist.txt
@@ -2069,6 +2069,7 @@ CONFIG_MXC_EPDC
CONFIG_MXC_GPIO
CONFIG_MXC_GPT_HCLK
CONFIG_MXC_MCI_REGS_BASE
+CONFIG_MXC_MIPI_DSI_NORTHWEST
CONFIG_MXC_NAND_HWECC
CONFIG_MXC_NAND_IP_REGS_BASE
CONFIG_MXC_NAND_REGS_BASE