summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiu Ying <Ying.Liu@freescale.com>2013-08-16 13:22:56 +0800
committerJason Liu <r64343@freescale.com>2013-10-30 09:54:53 +0800
commit4f49cdef96ff28917d6e74afc0805a3fbeb12cd0 (patch)
tree0a809fee3e1ec8a00e649e8392e5d008d7d06ce2
parent5ad5a15edaa4a530362ea759a2788fb1cd2f1cab (diff)
ENGR00275483-2 mxc fb: port mipi dsi drivers from 3.0.35 to 3.10
This patch is a fastforward porting for mipi dsi driver and TRULY-WVGA mipi panel driver. * Add devicetree support for the mipi dsi driver. * Get mipi config clock source in the mipi dsi driver. * Change clk_enable()/clk_disable() to clk_prepare_enable()/ clk_disable_unprepare(). * Move mipi dsi driver initialization stuff to mipi_dsi_probe() from mipi_dsi_disp_init(). * Move mach/mipi_dsi.h to linux/mipi_dsi.h. * Remove __devexit_p and __devexit since 3.10 kernel doesn't support them. * Remove struct mipi_dsi_platform_data from mach/mipi_dsi.h. * Remove mach/hardware.h, mach/clock.h, linux/fsl_device.h and linux/regulator/consumer.h in the drivers. * Remove special pixel clock setting for imx6sdl since no issue is found without it. * Remove unused reset, lcd_power and backlight_power interfaces in struct mipi_dsi_info. * Remove unused regulator related entries in struct mipi_dsi_info. * Remove the IOMUX_GPR3_OFFSET macro from mipi dsi driver since the muxing is done with common regmap APIs. * Rename ipu_id to dev_id since i.MX6DL RM tells that lcdif can connect with mipi dsi. * Add mipi dsi related information in the devicetree binding documentation. Signed-off-by: Liu Ying <Ying.Liu@freescale.com>
-rw-r--r--Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt28
-rw-r--r--drivers/video/mxc/Kconfig10
-rw-r--r--drivers/video/mxc/Makefile2
-rw-r--r--drivers/video/mxc/mipi_dsi.c953
-rw-r--r--drivers/video/mxc/mipi_dsi.h112
-rw-r--r--drivers/video/mxc/mxcfb_hx8369_wvga.c449
-rw-r--r--include/linux/mipi_dsi.h171
7 files changed, 1725 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt b/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt
index 629d4d91ec34..3a59da799620 100644
--- a/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt
+++ b/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt
@@ -59,6 +59,10 @@ Required properties for fb:
Required properties for display:
- compatible : should be "fsl,lcd" for lcd panel, "fsl,imx6q-ldb" for ldb
+- reg : the register address range if necessary to have.
+- interrupts : the error and sync interrupts if necessary to have.
+- clocks : the clock sources that it depends on if necessary to have.
+- clock-names: the related clock names if necessary to have.
- ipu_id : ipu id for the first display device: 0 or 1
- disp_id : display interface id for the first display interface: 0 or 1
- default_ifmt : save as above display interface pixel format for lcd
@@ -78,6 +82,14 @@ Required properties for display:
sin1 LDB_SIN1
sep0 LDB_SEP0
sep1 LDB_SEP1
+- gpr : the mux controller for the display engine's display interfaces and the display encoder
+ (only valid for mipi dsi now).
+- disp-power-on-supply : the regulator to control display panel's power.
+ (only valid for mipi dsi now).
+- resets : the gpio pin to reset the display device(only valid for mipi display panel now).
+- lcd_panel : the video mode name for the display device(only valid for mipi display panel now).
+- dev_id : the display engine's identity within the system, which intends to replace ipu_id
+ (only valid for mipi dsi now).
Example for IPU:
ipu1: ipu@02400000 {
@@ -116,3 +128,19 @@ Example for ldb display:
sec_disp_id = <1>;
status = "okay";
};
+
+Example for mipi dsi display:
+ mipi_dsi: mipi@021e0000 {
+ compatible = "fsl,imx6q-mipi-dsi";
+ reg = <0x021e0000 0x4000>;
+ interrupts = <0 102 0x04>;
+ gpr = <&gpr>;
+ clocks = <&clks 138>, <&clks 204>;
+ clock-names = "mipi_pllref_clk", "mipi_cfg_clk";
+ dev_id = <0>;
+ disp_id = <0>;
+ lcd_panel = "TRULY-WVGA";
+ disp-power-on-supply = <&reg_mipi_dsi_pwr_on>
+ resets = <&mipi_dsi_reset>;
+ status = "okay";
+ };
diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig
index 3e4dcb1a6dea..c8c2c6b5e23a 100644
--- a/drivers/video/mxc/Kconfig
+++ b/drivers/video/mxc/Kconfig
@@ -23,6 +23,16 @@ config FB_MXC_LDB
depends on FB_MXC_SYNC_PANEL
depends on MXC_IPU_V3
+config FB_MXC_MIPI_DSI
+ tristate "MXC MIPI_DSI"
+ depends on FB_MXC_SYNC_PANEL
+ depends on MXC_IPU_V3
+
+config FB_MXC_TRULY_WVGA_SYNC_PANEL
+ tristate "TRULY WVGA Panel"
+ depends on FB_MXC_SYNC_PANEL
+ depends on FB_MXC_MIPI_DSI
+
config FB_MXC_HDMI
depends on FB_MXC_SYNC_PANEL
depends on MXC_IPU_V3
diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile
index b6148e81ca72..4fbb8aa656f1 100644
--- a/drivers/video/mxc/Makefile
+++ b/drivers/video/mxc/Makefile
@@ -1,4 +1,6 @@
obj-$(CONFIG_FB_MXC_LDB) += ldb.o
+obj-$(CONFIG_FB_MXC_MIPI_DSI) += mipi_dsi.o
+obj-$(CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL) += mxcfb_hx8369_wvga.o
obj-$(CONFIG_FB_MXC_HDMI) += mxc_hdmi.o
obj-$(CONFIG_FB_MXC_EDID) += mxc_edid.o
obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_dispdrv.o mxc_lcdif.o mxc_ipuv3_fb.o
diff --git a/drivers/video/mxc/mipi_dsi.c b/drivers/video/mxc/mipi_dsi.c
new file mode 100644
index 000000000000..8517d2ff7302
--- /dev/null
+++ b/drivers/video/mxc/mipi_dsi.c
@@ -0,0 +1,953 @@
+/*
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * 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.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/ipu.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/mipi_dsi.h>
+#include <linux/module.h>
+#include <linux/mxcfb.h>
+#include <linux/backlight.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <video/mipi_display.h>
+
+#include "mxc_dispdrv.h"
+#include "mipi_dsi.h"
+
+#define DISPDRV_MIPI "mipi_dsi"
+#define ROUND_UP(x) ((x)+1)
+#define NS2PS_RATIO (1000)
+#define NUMBER_OF_CHUNKS (0x8)
+#define NULL_PKT_SIZE (0x8)
+#define PHY_BTA_MAXTIME (0xd00)
+#define PHY_LP2HS_MAXTIME (0x40)
+#define PHY_HS2LP_MAXTIME (0x40)
+#define PHY_STOP_WAIT_TIME (0x20)
+#define DSI_CLKMGR_CFG_CLK_DIV (0x107)
+#define DSI_GEN_PLD_DATA_BUF_ENTRY (0x10)
+#define MIPI_MUX_CTRL(v) (((v) & 0x3) << 4)
+#define MIPI_LCD_SLEEP_MODE_DELAY (120)
+#define MIPI_DSI_REG_RW_TIMEOUT (20)
+#define MIPI_DSI_PHY_TIMEOUT (10)
+
+static struct mipi_dsi_match_lcd mipi_dsi_lcd_db[] = {
+#ifdef CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL
+ {
+ "TRULY-WVGA",
+ {mipid_hx8369_get_lcd_videomode, mipid_hx8369_lcd_setup}
+ },
+#endif
+ {
+ "", {NULL, NULL}
+ }
+};
+
+struct _mipi_dsi_phy_pll_clk {
+ u32 max_phy_clk;
+ u32 config;
+};
+
+/* configure data for DPHY PLL 27M reference clk out */
+static const struct _mipi_dsi_phy_pll_clk mipi_dsi_phy_pll_clk_table[] = {
+ {1000, 0x74}, /* 950-1000MHz */
+ {950, 0x54}, /* 900-950Mhz */
+ {900, 0x34}, /* 850-900Mhz */
+ {850, 0x14}, /* 800-850MHz */
+ {800, 0x32}, /* 750-800MHz */
+ {750, 0x12}, /* 700-750Mhz */
+ {700, 0x30}, /* 650-700Mhz */
+ {650, 0x10}, /* 600-650MHz */
+ {600, 0x2e}, /* 550-600MHz */
+ {550, 0x0e}, /* 500-550Mhz */
+ {500, 0x2c}, /* 450-500Mhz */
+ {450, 0x0c}, /* 400-450MHz */
+ {400, 0x4a}, /* 360-400MHz */
+ {360, 0x2a}, /* 330-360Mhz */
+ {330, 0x48}, /* 300-330Mhz */
+ {300, 0x28}, /* 270-300MHz */
+ {270, 0x08}, /* 250-270MHz */
+ {250, 0x46}, /* 240-250Mhz */
+ {240, 0x26}, /* 210-240Mhz */
+ {210, 0x06}, /* 200-210MHz */
+ {200, 0x44}, /* 180-200MHz */
+ {180, 0x24}, /* 160-180MHz */
+ {160, 0x04}, /* 150-160MHz */
+};
+
+static int valid_mode(int pixel_fmt)
+{
+ return ((pixel_fmt == IPU_PIX_FMT_RGB24) ||
+ (pixel_fmt == IPU_PIX_FMT_BGR24) ||
+ (pixel_fmt == IPU_PIX_FMT_RGB666) ||
+ (pixel_fmt == IPU_PIX_FMT_RGB565) ||
+ (pixel_fmt == IPU_PIX_FMT_BGR666) ||
+ (pixel_fmt == IPU_PIX_FMT_RGB332));
+}
+
+static inline void mipi_dsi_read_register(struct mipi_dsi_info *mipi_dsi,
+ u32 reg, u32 *val)
+{
+ *val = ioread32(mipi_dsi->mmio_base + reg);
+ dev_dbg(&mipi_dsi->pdev->dev, "read_reg:0x%02x, val:0x%08x.\n",
+ reg, *val);
+}
+
+static inline void mipi_dsi_write_register(struct mipi_dsi_info *mipi_dsi,
+ u32 reg, u32 val)
+{
+ iowrite32(val, mipi_dsi->mmio_base + reg);
+ dev_dbg(&mipi_dsi->pdev->dev, "\t\twrite_reg:0x%02x, val:0x%08x.\n",
+ reg, val);
+}
+
+int mipi_dsi_pkt_write(struct mipi_dsi_info *mipi_dsi,
+ u8 data_type, const u32 *buf, int len)
+{
+ u32 val;
+ u32 status = 0;
+ int write_len = len;
+ uint32_t timeout = 0;
+
+ if (len) {
+ /* generic long write command */
+ while (len / DSI_GEN_PLD_DATA_BUF_SIZE) {
+ mipi_dsi_write_register(mipi_dsi,
+ MIPI_DSI_GEN_PLD_DATA, *buf);
+ buf++;
+ len -= DSI_GEN_PLD_DATA_BUF_SIZE;
+ mipi_dsi_read_register(mipi_dsi,
+ MIPI_DSI_CMD_PKT_STATUS, &status);
+ while ((status & DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) ==
+ DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi,
+ MIPI_DSI_CMD_PKT_STATUS, &status);
+ }
+ }
+ /* write the remainder bytes */
+ if (len > 0) {
+ while ((status & DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) ==
+ DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi,
+ MIPI_DSI_CMD_PKT_STATUS, &status);
+ }
+ mipi_dsi_write_register(mipi_dsi,
+ MIPI_DSI_GEN_PLD_DATA, *buf);
+ }
+
+ val = data_type | ((write_len & DSI_GEN_HDR_DATA_MASK)
+ << DSI_GEN_HDR_DATA_SHIFT);
+ } else {
+ /* generic short write command */
+ val = data_type | ((*buf & DSI_GEN_HDR_DATA_MASK)
+ << DSI_GEN_HDR_DATA_SHIFT);
+ }
+
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &status);
+ while ((status & DSI_CMD_PKT_STATUS_GEN_CMD_FULL) ==
+ DSI_CMD_PKT_STATUS_GEN_CMD_FULL) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
+ &status);
+ }
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_GEN_HDR, val);
+
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &status);
+ while (!((status & DSI_CMD_PKT_STATUS_GEN_CMD_EMPTY) ==
+ DSI_CMD_PKT_STATUS_GEN_CMD_EMPTY) ||
+ !((status & DSI_CMD_PKT_STATUS_GEN_PLD_W_EMPTY) ==
+ DSI_CMD_PKT_STATUS_GEN_PLD_W_EMPTY)) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
+ &status);
+ }
+
+ return 0;
+}
+
+int mipi_dsi_pkt_read(struct mipi_dsi_info *mipi_dsi,
+ u8 data_type, u32 *buf, int len)
+{
+ u32 val;
+ int read_len = 0;
+ uint32_t timeout = 0;
+
+ if (!len) {
+ mipi_dbg("%s, len = 0 invalid error!\n", __func__);
+ return -EINVAL;
+ }
+
+ val = data_type | ((*buf & DSI_GEN_HDR_DATA_MASK)
+ << DSI_GEN_HDR_DATA_SHIFT);
+ memset(buf, 0, len);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_GEN_HDR, val);
+
+ /* wait for cmd to sent out */
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &val);
+ while ((val & DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) !=
+ DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
+ &val);
+ }
+ /* wait for entire response stroed in FIFO */
+ while ((val & DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) ==
+ DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
+ &val);
+ }
+
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &val);
+ while (!(val & DSI_CMD_PKT_STATUS_GEN_PLD_R_EMPTY)) {
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_GEN_PLD_DATA, buf);
+ read_len += DSI_GEN_PLD_DATA_BUF_SIZE;
+ buf++;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
+ &val);
+ if (read_len == (DSI_GEN_PLD_DATA_BUF_ENTRY *
+ DSI_GEN_PLD_DATA_BUF_SIZE))
+ break;
+ }
+
+ if ((len <= read_len) &&
+ ((len + DSI_GEN_PLD_DATA_BUF_SIZE) >= read_len))
+ return 0;
+ else {
+ dev_err(&mipi_dsi->pdev->dev,
+ "actually read_len:%d != len:%d.\n", read_len, len);
+ return -ERANGE;
+ }
+}
+
+int mipi_dsi_dcs_cmd(struct mipi_dsi_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;
+ err = mipi_dsi_pkt_write(mipi_dsi,
+ MIPI_DSI_DCS_SHORT_WRITE, buf, 0);
+ break;
+
+ default:
+ dev_err(&mipi_dsi->pdev->dev,
+ "MIPI DSI DCS Command:0x%x Not supported!\n", cmd);
+ break;
+ }
+
+ return err;
+}
+
+static void mipi_dsi_dphy_init(struct mipi_dsi_info *mipi_dsi,
+ u32 cmd, u32 data)
+{
+ u32 val;
+ u32 timeout = 0;
+
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL,
+ DSI_PHY_IF_CTRL_RESET);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP, DSI_PWRUP_POWERUP);
+
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 0);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL1,
+ (0x10000 | cmd));
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 2);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 0);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL1, (0 | data));
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 2);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 0);
+ val = DSI_PHY_RSTZ_EN_CLK | DSI_PHY_RSTZ_DISABLE_RST |
+ DSI_PHY_RSTZ_DISABLE_SHUTDOWN;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_RSTZ, val);
+
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_PHY_STATUS, &val);
+ while ((val & DSI_PHY_STATUS_LOCK) != DSI_PHY_STATUS_LOCK) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_PHY_TIMEOUT) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "Error: phy lock timeout!\n");
+ break;
+ }
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_PHY_STATUS, &val);
+ }
+ timeout = 0;
+ while ((val & DSI_PHY_STATUS_STOPSTATE_CLK_LANE) !=
+ DSI_PHY_STATUS_STOPSTATE_CLK_LANE) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_PHY_TIMEOUT) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "Error: phy lock lane timeout!\n");
+ break;
+ }
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_PHY_STATUS, &val);
+ }
+}
+
+static void mipi_dsi_enable_controller(struct mipi_dsi_info *mipi_dsi,
+ bool init)
+{
+ u32 val;
+ u32 lane_byte_clk_period;
+ struct fb_videomode *mode = mipi_dsi->mode;
+ struct mipi_lcd_config *lcd_config = mipi_dsi->lcd_config;
+
+ if (init) {
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
+ DSI_PWRUP_RESET);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_RSTZ,
+ DSI_PHY_RSTZ_RST);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CLKMGR_CFG,
+ DSI_CLKMGR_CFG_CLK_DIV);
+
+ if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
+ val = DSI_DPI_CFG_VSYNC_ACT_LOW;
+ if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
+ val |= DSI_DPI_CFG_HSYNC_ACT_LOW;
+ if ((mode->sync & FB_SYNC_OE_LOW_ACT))
+ val |= DSI_DPI_CFG_DATAEN_ACT_LOW;
+ if (MIPI_RGB666_LOOSELY == lcd_config->dpi_fmt)
+ val |= DSI_DPI_CFG_EN18LOOSELY;
+ val |= (lcd_config->dpi_fmt & DSI_DPI_CFG_COLORCODE_MASK)
+ << DSI_DPI_CFG_COLORCODE_SHIFT;
+ val |= (lcd_config->virtual_ch & DSI_DPI_CFG_VID_MASK)
+ << DSI_DPI_CFG_VID_SHIFT;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_DPI_CFG, val);
+
+ val = DSI_PCKHDL_CFG_EN_BTA |
+ DSI_PCKHDL_CFG_EN_ECC_RX |
+ DSI_PCKHDL_CFG_EN_CRC_RX;
+
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PCKHDL_CFG, val);
+
+ val = (mode->xres & DSI_VID_PKT_CFG_VID_PKT_SZ_MASK)
+ << DSI_VID_PKT_CFG_VID_PKT_SZ_SHIFT;
+ val |= (NUMBER_OF_CHUNKS & DSI_VID_PKT_CFG_NUM_CHUNKS_MASK)
+ << DSI_VID_PKT_CFG_NUM_CHUNKS_SHIFT;
+ val |= (NULL_PKT_SIZE & DSI_VID_PKT_CFG_NULL_PKT_SZ_MASK)
+ << DSI_VID_PKT_CFG_NULL_PKT_SZ_SHIFT;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_PKT_CFG, val);
+
+ /* enable LP mode when TX DCS cmd and enable DSI command mode */
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG,
+ MIPI_DSI_CMD_MODE_CFG_EN_LOWPOWER);
+
+ /* mipi lane byte clk period in ns unit */
+ lane_byte_clk_period = NS2PS_RATIO /
+ (lcd_config->max_phy_clk / BITS_PER_BYTE);
+ val = ROUND_UP(mode->hsync_len * mode->pixclock /
+ NS2PS_RATIO / lane_byte_clk_period)
+ << DSI_TME_LINE_CFG_HSA_TIME_SHIFT;
+ val |= ROUND_UP(mode->left_margin * mode->pixclock /
+ NS2PS_RATIO / lane_byte_clk_period)
+ << DSI_TME_LINE_CFG_HBP_TIME_SHIFT;
+ val |= ROUND_UP((mode->left_margin + mode->right_margin +
+ mode->hsync_len + mode->xres) * mode->pixclock
+ / NS2PS_RATIO / lane_byte_clk_period)
+ << DSI_TME_LINE_CFG_HLINE_TIME_SHIFT;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_TMR_LINE_CFG, val);
+
+ val = ((mode->vsync_len & DSI_VTIMING_CFG_VSA_LINES_MASK)
+ << DSI_VTIMING_CFG_VSA_LINES_SHIFT);
+ val |= ((mode->upper_margin & DSI_VTIMING_CFG_VBP_LINES_MASK)
+ << DSI_VTIMING_CFG_VBP_LINES_SHIFT);
+ val |= ((mode->lower_margin & DSI_VTIMING_CFG_VFP_LINES_MASK)
+ << DSI_VTIMING_CFG_VFP_LINES_SHIFT);
+ val |= ((mode->yres & DSI_VTIMING_CFG_V_ACT_LINES_MASK)
+ << DSI_VTIMING_CFG_V_ACT_LINES_SHIFT);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VTIMING_CFG, val);
+
+ val = ((PHY_BTA_MAXTIME & DSI_PHY_TMR_CFG_BTA_TIME_MASK)
+ << DSI_PHY_TMR_CFG_BTA_TIME_SHIFT);
+ val |= ((PHY_LP2HS_MAXTIME & DSI_PHY_TMR_CFG_LP2HS_TIME_MASK)
+ << DSI_PHY_TMR_CFG_LP2HS_TIME_SHIFT);
+ val |= ((PHY_HS2LP_MAXTIME & DSI_PHY_TMR_CFG_HS2LP_TIME_MASK)
+ << DSI_PHY_TMR_CFG_HS2LP_TIME_SHIFT);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TMR_CFG, val);
+
+ val = (((lcd_config->data_lane_num - 1) &
+ DSI_PHY_IF_CFG_N_LANES_MASK)
+ << DSI_PHY_IF_CFG_N_LANES_SHIFT);
+ val |= ((PHY_STOP_WAIT_TIME & DSI_PHY_IF_CFG_WAIT_TIME_MASK)
+ << DSI_PHY_IF_CFG_WAIT_TIME_SHIFT);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CFG, val);
+
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST0, &val);
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST1, &val);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_ERROR_MSK0, 0);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_ERROR_MSK1, 0);
+
+ mipi_dsi_dphy_init(mipi_dsi, DSI_PHY_CLK_INIT_COMMAND,
+ mipi_dsi->dphy_pll_config);
+ } else {
+ mipi_dsi_dphy_init(mipi_dsi, DSI_PHY_CLK_INIT_COMMAND,
+ mipi_dsi->dphy_pll_config);
+ }
+}
+
+static void mipi_dsi_disable_controller(struct mipi_dsi_info *mipi_dsi)
+{
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL,
+ DSI_PHY_IF_CTRL_RESET);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP, DSI_PWRUP_RESET);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_RSTZ, DSI_PHY_RSTZ_RST);
+}
+
+static irqreturn_t mipi_dsi_irq_handler(int irq, void *data)
+{
+ u32 mask0;
+ u32 mask1;
+ u32 status0;
+ u32 status1;
+ struct mipi_dsi_info *mipi_dsi;
+
+ mipi_dsi = (struct mipi_dsi_info *)data;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST0, &status0);
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST1, &status1);
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_MSK0, &mask0);
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_MSK1, &mask1);
+
+ if ((status0 & (~mask0)) || (status1 & (~mask1))) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "mipi_dsi IRQ status0:0x%x, status1:0x%x!\n",
+ status0, status1);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static inline void mipi_dsi_set_mode(struct mipi_dsi_info *mipi_dsi,
+ bool cmd_mode)
+{
+ u32 val;
+
+ if (cmd_mode) {
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
+ DSI_PWRUP_RESET);
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, &val);
+ val |= MIPI_DSI_CMD_MODE_CFG_EN_CMD_MODE;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, val);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_MODE_CFG, 0);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
+ DSI_PWRUP_POWERUP);
+ } else {
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
+ DSI_PWRUP_RESET);
+ /* Disable Command mode when tranfering video data */
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, &val);
+ val &= ~MIPI_DSI_CMD_MODE_CFG_EN_CMD_MODE;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, val);
+ val = DSI_VID_MODE_CFG_EN | DSI_VID_MODE_CFG_EN_BURSTMODE |
+ DSI_VID_MODE_CFG_EN_LP_MODE;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_MODE_CFG, val);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
+ DSI_PWRUP_POWERUP);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL,
+ DSI_PHY_IF_CTRL_TX_REQ_CLK_HS);
+ }
+}
+
+static int mipi_dsi_power_on(struct mxc_dispdrv_handle *disp)
+{
+ int err;
+ struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
+
+ if (!mipi_dsi->dsi_power_on) {
+ clk_prepare_enable(mipi_dsi->dphy_clk);
+ clk_prepare_enable(mipi_dsi->cfg_clk);
+ mipi_dsi_enable_controller(mipi_dsi, false);
+ mipi_dsi_set_mode(mipi_dsi, false);
+ /* host send pclk/hsync/vsync for two frames before sleep-out */
+ msleep((1000/mipi_dsi->mode->refresh + 1) << 1);
+ mipi_dsi_set_mode(mipi_dsi, true);
+ err = mipi_dsi_dcs_cmd(mipi_dsi, MIPI_DCS_EXIT_SLEEP_MODE,
+ NULL, 0);
+ if (err) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "MIPI DSI DCS Command sleep-in error!\n");
+ }
+ msleep(MIPI_LCD_SLEEP_MODE_DELAY);
+ mipi_dsi_set_mode(mipi_dsi, false);
+ mipi_dsi->dsi_power_on = 1;
+ }
+
+ return 0;
+}
+
+void mipi_dsi_power_off(struct mxc_dispdrv_handle *disp)
+{
+ int err;
+ struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
+
+ if (mipi_dsi->dsi_power_on) {
+ mipi_dsi_set_mode(mipi_dsi, true);
+ err = mipi_dsi_dcs_cmd(mipi_dsi, MIPI_DCS_ENTER_SLEEP_MODE,
+ NULL, 0);
+ if (err) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "MIPI DSI DCS Command display on error!\n");
+ }
+ /* To allow time for the supply voltages
+ * and clock circuits to stabilize.
+ */
+ msleep(5);
+ /* video stream timing on */
+ mipi_dsi_set_mode(mipi_dsi, false);
+ msleep(MIPI_LCD_SLEEP_MODE_DELAY);
+
+ mipi_dsi_set_mode(mipi_dsi, true);
+ mipi_dsi_disable_controller(mipi_dsi);
+ mipi_dsi->dsi_power_on = 0;
+ clk_disable_unprepare(mipi_dsi->dphy_clk);
+ clk_disable_unprepare(mipi_dsi->cfg_clk);
+ }
+}
+
+static int mipi_dsi_lcd_init(struct mipi_dsi_info *mipi_dsi,
+ struct mxc_dispdrv_setting *setting)
+{
+ int err;
+ int size;
+ int i;
+ struct fb_videomode *mipi_lcd_modedb;
+ struct fb_videomode mode;
+ struct device *dev = &mipi_dsi->pdev->dev;
+
+ for (i = 0; i < ARRAY_SIZE(mipi_dsi_lcd_db); i++) {
+ if (!strcmp(mipi_dsi->lcd_panel,
+ mipi_dsi_lcd_db[i].lcd_panel)) {
+ mipi_dsi->lcd_callback =
+ &mipi_dsi_lcd_db[i].lcd_callback;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(mipi_dsi_lcd_db)) {
+ dev_err(dev, "failed to find supported lcd panel.\n");
+ return -EINVAL;
+ }
+ /* get the videomode in the order: cmdline->platform data->driver */
+ mipi_dsi->lcd_callback->get_mipi_lcd_videomode(&mipi_lcd_modedb, &size,
+ &mipi_dsi->lcd_config);
+ err = fb_find_mode(&setting->fbi->var, setting->fbi,
+ setting->dft_mode_str,
+ mipi_lcd_modedb, size, NULL,
+ setting->default_bpp);
+ if (err != 1)
+ fb_videomode_to_var(&setting->fbi->var, mipi_lcd_modedb);
+
+ INIT_LIST_HEAD(&setting->fbi->modelist);
+ for (i = 0; i < size; i++) {
+ fb_var_to_videomode(&mode, &setting->fbi->var);
+ if (fb_mode_is_equal(&mode, mipi_lcd_modedb + i)) {
+ err = fb_add_videomode(mipi_lcd_modedb + i,
+ &setting->fbi->modelist);
+ /* Note: only support fb mode from driver */
+ mipi_dsi->mode = mipi_lcd_modedb + i;
+ break;
+ }
+ }
+ if ((err < 0) || (size == i)) {
+ dev_err(dev, "failed to add videomode.\n");
+ return err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mipi_dsi_phy_pll_clk_table); i++) {
+ if (mipi_dsi_phy_pll_clk_table[i].max_phy_clk <
+ mipi_dsi->lcd_config->max_phy_clk)
+ break;
+ }
+ if ((i == ARRAY_SIZE(mipi_dsi_phy_pll_clk_table)) ||
+ (mipi_dsi->lcd_config->max_phy_clk >
+ mipi_dsi_phy_pll_clk_table[0].max_phy_clk)) {
+ dev_err(dev, "failed to find data in"
+ "mipi_dsi_phy_pll_clk_table.\n");
+ return -EINVAL;
+ }
+ mipi_dsi->dphy_pll_config = mipi_dsi_phy_pll_clk_table[--i].config;
+ dev_dbg(dev, "dphy_pll_config:0x%x.\n", mipi_dsi->dphy_pll_config);
+
+ return 0;
+}
+
+int mipi_dsi_enable(struct mxc_dispdrv_handle *disp)
+{
+ int err;
+ struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
+
+ if (!mipi_dsi->lcd_inited) {
+ err = clk_prepare_enable(mipi_dsi->dphy_clk);
+ err |= clk_prepare_enable(mipi_dsi->cfg_clk);
+ if (err)
+ dev_err(&mipi_dsi->pdev->dev,
+ "clk enable error:%d!\n", err);
+ mipi_dsi_enable_controller(mipi_dsi, true);
+ err = mipi_dsi->lcd_callback->mipi_lcd_setup(
+ mipi_dsi);
+ if (err < 0) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "failed to init mipi lcd.");
+ clk_disable_unprepare(mipi_dsi->dphy_clk);
+ clk_disable_unprepare(mipi_dsi->cfg_clk);
+ return err;
+ }
+ mipi_dsi_set_mode(mipi_dsi, false);
+ mipi_dsi->dsi_power_on = 1;
+ mipi_dsi->lcd_inited = 1;
+ }
+ mipi_dsi_power_on(mipi_dsi->disp_mipi);
+
+ return 0;
+}
+
+static int mipi_dsi_disp_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
+{
+ struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
+ struct device *dev = &mipi_dsi->pdev->dev;
+ int ret = 0;
+
+ if (!valid_mode(setting->if_fmt)) {
+ dev_warn(dev, "Input pixel format not valid"
+ "use default RGB24\n");
+ setting->if_fmt = IPU_PIX_FMT_RGB24;
+ }
+
+ setting->dev_id = mipi_dsi->dev_id;
+ setting->disp_id = mipi_dsi->disp_id;
+
+ ret = mipi_dsi_lcd_init(mipi_dsi, setting);
+ if (ret) {
+ dev_err(dev, "failed to init mipi dsi lcd\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "MIPI DSI dispdrv inited!\n");
+ return ret;
+}
+
+static void mipi_dsi_disp_deinit(struct mxc_dispdrv_handle *disp)
+{
+ struct mipi_dsi_info *mipi_dsi;
+
+ mipi_dsi = mxc_dispdrv_getdata(disp);
+
+ mipi_dsi_power_off(mipi_dsi->disp_mipi);
+ if (mipi_dsi->bl)
+ backlight_device_unregister(mipi_dsi->bl);
+}
+
+static struct mxc_dispdrv_driver mipi_dsi_drv = {
+ .name = DISPDRV_MIPI,
+ .init = mipi_dsi_disp_init,
+ .deinit = mipi_dsi_disp_deinit,
+ .enable = mipi_dsi_enable,
+ .disable = mipi_dsi_power_off,
+};
+
+static int imx6q_mipi_dsi_get_mux(int dev_id, int disp_id)
+{
+ if (dev_id > 1 || disp_id > 1)
+ return -EINVAL;
+
+ return (dev_id << 5) | (disp_id << 4);
+}
+
+static struct mipi_dsi_bus_mux imx6q_mipi_dsi_mux[] = {
+ {
+ .reg = IOMUXC_GPR3,
+ .mask = IMX6Q_GPR3_MIPI_MUX_CTL_MASK,
+ .get_mux = imx6q_mipi_dsi_get_mux,
+ },
+};
+
+static int imx6dl_mipi_dsi_get_mux(int dev_id, int disp_id)
+{
+ if (dev_id > 1 || disp_id > 1)
+ return -EINVAL;
+
+ /* MIPI DSI source is LCDIF */
+ if (dev_id)
+ disp_id = 0;
+
+ return (dev_id << 5) | (disp_id << 4);
+}
+
+static struct mipi_dsi_bus_mux imx6dl_mipi_dsi_mux[] = {
+ {
+ .reg = IOMUXC_GPR3,
+ .mask = IMX6Q_GPR3_MIPI_MUX_CTL_MASK,
+ .get_mux = imx6dl_mipi_dsi_get_mux,
+ },
+};
+
+static const struct of_device_id imx_mipi_dsi_dt_ids[] = {
+ { .compatible = "fsl,imx6q-mipi-dsi", .data = imx6q_mipi_dsi_mux, },
+ { .compatible = "fsl,imx6dl-mipi-dsi", .data = imx6dl_mipi_dsi_mux, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, imx_mipi_dsi_dt_ids);
+
+/**
+ * This function is called by the driver framework to initialize the MIPI DSI
+ * device.
+ *
+ * @param pdev The device structure for the MIPI DSI passed in by the
+ * driver framework.
+ *
+ * @return Returns 0 on success or negative error code on error
+ */
+static int mipi_dsi_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(of_match_ptr(imx_mipi_dsi_dt_ids),
+ &pdev->dev);
+ struct mipi_dsi_info *mipi_dsi;
+ struct resource *res;
+ u32 dev_id, disp_id;
+ const char *lcd_panel;
+ unsigned int mux;
+ int ret = 0;
+
+ mipi_dsi = devm_kzalloc(&pdev->dev, sizeof(*mipi_dsi), GFP_KERNEL);
+ if (!mipi_dsi)
+ return -ENOMEM;
+
+ ret = of_property_read_string(np, "lcd_panel", &lcd_panel);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read of property lcd_panel\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "dev_id", &dev_id);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read of property dev_id\n");
+ return ret;
+ }
+ ret = of_property_read_u32(np, "disp_id", &disp_id);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read of property disp_id\n");
+ return ret;
+ }
+ mipi_dsi->dev_id = dev_id;
+ mipi_dsi->disp_id = disp_id;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get platform resource 0\n");
+ return -ENODEV;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name))
+ return -EBUSY;
+
+ mipi_dsi->mmio_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!mipi_dsi->mmio_base)
+ return -EBUSY;
+
+ mipi_dsi->irq = platform_get_irq(pdev, 0);
+ if (mipi_dsi->irq < 0) {
+ dev_err(&pdev->dev, "failed get device irq\n");
+ return -ENODEV;
+ }
+
+ ret = devm_request_irq(&pdev->dev, mipi_dsi->irq,
+ mipi_dsi_irq_handler,
+ 0, "mipi_dsi", mipi_dsi);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return ret;
+ }
+
+ mipi_dsi->dphy_clk = devm_clk_get(&pdev->dev, "mipi_pllref_clk");
+ if (IS_ERR(mipi_dsi->dphy_clk)) {
+ dev_err(&pdev->dev, "failed to get dphy pll_ref_clk\n");
+ return PTR_ERR(mipi_dsi->dphy_clk);
+ }
+
+ mipi_dsi->cfg_clk = devm_clk_get(&pdev->dev, "mipi_cfg_clk");
+ if (IS_ERR(mipi_dsi->cfg_clk)) {
+ dev_err(&pdev->dev, "failed to get cfg_clk\n");
+ return PTR_ERR(mipi_dsi->cfg_clk);
+ }
+
+ mipi_dsi->disp_power_on = devm_regulator_get(&pdev->dev,
+ "disp-power-on");
+ if (!IS_ERR(mipi_dsi->disp_power_on)) {
+ ret = regulator_enable(mipi_dsi->disp_power_on);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable display "
+ "power regulator, err=%d\n", ret);
+ return ret;
+ }
+ } else {
+ mipi_dsi->disp_power_on = NULL;
+ }
+
+ ret = device_reset(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to reset: %d\n", ret);
+ goto dev_reset_fail;
+ }
+
+ if (of_id)
+ mipi_dsi->bus_mux = of_id->data;
+
+ mipi_dsi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
+ if (IS_ERR(mipi_dsi->regmap)) {
+ dev_err(&pdev->dev, "failed to get parent regmap\n");
+ ret = PTR_ERR(mipi_dsi->regmap);
+ goto get_parent_regmap_fail;
+ }
+
+ mux = mipi_dsi->bus_mux->get_mux(dev_id, disp_id);
+ if (mux >= 0)
+ regmap_update_bits(mipi_dsi->regmap, mipi_dsi->bus_mux->reg,
+ mipi_dsi->bus_mux->mask, mux);
+ else
+ dev_warn(&pdev->dev, "invalid dev_id or disp_id muxing\n");
+
+ mipi_dsi->lcd_panel = kstrdup(lcd_panel, GFP_KERNEL);
+ if (!mipi_dsi->lcd_panel) {
+ dev_err(&pdev->dev, "failed to allocate lcd panel name\n");
+ ret = -ENOMEM;
+ goto kstrdup_fail;
+ }
+
+ mipi_dsi->pdev = pdev;
+ mipi_dsi->disp_mipi = mxc_dispdrv_register(&mipi_dsi_drv);
+ if (IS_ERR(mipi_dsi->disp_mipi)) {
+ dev_err(&pdev->dev, "mxc_dispdrv_register error\n");
+ ret = PTR_ERR(mipi_dsi->disp_mipi);
+ goto dispdrv_reg_fail;
+ }
+
+ mxc_dispdrv_setdata(mipi_dsi->disp_mipi, mipi_dsi);
+ dev_set_drvdata(&pdev->dev, mipi_dsi);
+
+ dev_info(&pdev->dev, "i.MX MIPI DSI driver probed\n");
+ return ret;
+
+dispdrv_reg_fail:
+ kfree(mipi_dsi->lcd_panel);
+kstrdup_fail:
+get_parent_regmap_fail:
+dev_reset_fail:
+ if (mipi_dsi->disp_power_on)
+ regulator_disable(mipi_dsi->disp_power_on);
+ return ret;
+}
+
+static void mipi_dsi_shutdown(struct platform_device *pdev)
+{
+ struct mipi_dsi_info *mipi_dsi = dev_get_drvdata(&pdev->dev);
+
+ mipi_dsi_power_off(mipi_dsi->disp_mipi);
+}
+
+static int mipi_dsi_remove(struct platform_device *pdev)
+{
+ struct mipi_dsi_info *mipi_dsi = dev_get_drvdata(&pdev->dev);
+
+ mxc_dispdrv_puthandle(mipi_dsi->disp_mipi);
+ mxc_dispdrv_unregister(mipi_dsi->disp_mipi);
+
+ if (mipi_dsi->disp_power_on)
+ regulator_disable(mipi_dsi->disp_power_on);
+
+ kfree(mipi_dsi->lcd_panel);
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver mipi_dsi_driver = {
+ .driver = {
+ .of_match_table = imx_mipi_dsi_dt_ids,
+ .name = "mxc_mipi_dsi",
+ },
+ .probe = mipi_dsi_probe,
+ .remove = mipi_dsi_remove,
+ .shutdown = mipi_dsi_shutdown,
+};
+
+static int __init mipi_dsi_init(void)
+{
+ int err;
+
+ err = platform_driver_register(&mipi_dsi_driver);
+ if (err) {
+ pr_err("mipi_dsi_driver register failed\n");
+ return -ENODEV;
+ }
+ pr_info("MIPI DSI driver module loaded\n");
+ return 0;
+}
+
+static void __exit mipi_dsi_cleanup(void)
+{
+ platform_driver_unregister(&mipi_dsi_driver);
+}
+
+module_init(mipi_dsi_init);
+module_exit(mipi_dsi_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX MIPI DSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mipi_dsi.h b/drivers/video/mxc/mipi_dsi.h
new file mode 100644
index 000000000000..098dea8f6d7f
--- /dev/null
+++ b/drivers/video/mxc/mipi_dsi.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * 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.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __MIPI_DSI_H__
+#define __MIPI_DSI_H__
+
+#include <linux/regmap.h>
+
+#ifdef DEBUG
+#define mipi_dbg(fmt, ...) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#else
+#define mipi_dbg(fmt, ...)
+#endif
+
+#define DSI_CMD_BUF_MAXSIZE (32)
+
+/* DPI interface pixel color coding map */
+enum mipi_dsi_dpi_fmt {
+ MIPI_RGB565_PACKED = 0,
+ MIPI_RGB565_LOOSELY,
+ MIPI_RGB565_CONFIG3,
+ MIPI_RGB666_PACKED,
+ MIPI_RGB666_LOOSELY,
+ MIPI_RGB888,
+};
+
+struct mipi_lcd_config {
+ u32 virtual_ch;
+ u32 data_lane_num;
+ /* device max DPHY clock in MHz unit */
+ u32 max_phy_clk;
+ enum mipi_dsi_dpi_fmt dpi_fmt;
+};
+
+struct mipi_dsi_info;
+struct mipi_dsi_lcd_callback {
+ /* callback for lcd panel operation */
+ void (*get_mipi_lcd_videomode)(struct fb_videomode **, int *,
+ struct mipi_lcd_config **);
+ int (*mipi_lcd_setup)(struct mipi_dsi_info *);
+
+};
+
+struct mipi_dsi_match_lcd {
+ char *lcd_panel;
+ struct mipi_dsi_lcd_callback lcd_callback;
+};
+
+struct mipi_dsi_bus_mux {
+ int reg;
+ int mask;
+ int (*get_mux) (int dev_id, int disp_id);
+};
+
+/* driver private data */
+struct mipi_dsi_info {
+ struct platform_device *pdev;
+ void __iomem *mmio_base;
+ struct regmap *regmap;
+ const struct mipi_dsi_bus_mux *bus_mux;
+ int dsi_power_on;
+ int lcd_inited;
+ u32 dphy_pll_config;
+ int dev_id;
+ int disp_id;
+ char *lcd_panel;
+ int irq;
+ struct clk *dphy_clk;
+ struct clk *cfg_clk;
+ struct mxc_dispdrv_handle *disp_mipi;
+ struct fb_videomode *mode;
+ struct regulator *disp_power_on;
+ struct mipi_lcd_config *lcd_config;
+ /* board related power control */
+ struct backlight_device *bl;
+ /* callback for lcd panel operation */
+ struct mipi_dsi_lcd_callback *lcd_callback;
+};
+
+int mipi_dsi_pkt_write(struct mipi_dsi_info *mipi,
+ u8 data_type, const u32 *buf, int len);
+int mipi_dsi_pkt_read(struct mipi_dsi_info *mipi,
+ u8 data_type, u32 *buf, int len);
+int mipi_dsi_dcs_cmd(struct mipi_dsi_info *mipi,
+ u8 cmd, const u32 *param, int num);
+
+#ifdef CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL
+void mipid_hx8369_get_lcd_videomode(struct fb_videomode **mode, int *size,
+ struct mipi_lcd_config **data);
+int mipid_hx8369_lcd_setup(struct mipi_dsi_info *);
+#endif
+
+#ifndef CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL
+#error "Please configure MIPI LCD panel, we cannot find one!"
+#endif
+
+#endif
diff --git a/drivers/video/mxc/mxcfb_hx8369_wvga.c b/drivers/video/mxc/mxcfb_hx8369_wvga.c
new file mode 100644
index 000000000000..56e4f2866304
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_hx8369_wvga.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * 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.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/mipi_dsi.h>
+#include <linux/mxcfb.h>
+#include <linux/backlight.h>
+#include <video/mipi_display.h>
+
+#include "mipi_dsi.h"
+
+#define MIPI_DSI_MAX_RET_PACK_SIZE (0x4)
+
+#define HX8369BL_MAX_BRIGHT (255)
+#define HX8369BL_DEF_BRIGHT (255)
+
+#define HX8369_MAX_DPHY_CLK (800)
+#define HX8369_ONE_DATA_LANE (0x1)
+#define HX8369_TWO_DATA_LANE (0x2)
+
+#define HX8369_CMD_SETEXTC (0xB9)
+#define HX8369_CMD_SETEXTC_LEN (0x4)
+#define HX8369_CMD_SETEXTC_PARAM_1 (0x6983ff)
+
+#define HX8369_CMD_GETHXID (0xF4)
+#define HX8369_CMD_GETHXID_LEN (0x4)
+#define HX8369_ID (0x69)
+#define HX8369_ID_MASK (0xFF)
+
+#define HX8369_CMD_SETDISP (0xB2)
+#define HX8369_CMD_SETDISP_LEN (16)
+#define HX8369_CMD_SETDISP_1_HALT (0x00)
+#define HX8369_CMD_SETDISP_2_RES_MODE (0x23)
+#define HX8369_CMD_SETDISP_3_BP (0x03)
+#define HX8369_CMD_SETDISP_4_FP (0x03)
+#define HX8369_CMD_SETDISP_5_SAP (0x70)
+#define HX8369_CMD_SETDISP_6_GENON (0x00)
+#define HX8369_CMD_SETDISP_7_GENOFF (0xff)
+#define HX8369_CMD_SETDISP_8_RTN (0x00)
+#define HX8369_CMD_SETDISP_9_TEI (0x00)
+#define HX8369_CMD_SETDISP_10_TEP_UP (0x00)
+#define HX8369_CMD_SETDISP_11_TEP_LOW (0x00)
+#define HX8369_CMD_SETDISP_12_BP_PE (0x03)
+#define HX8369_CMD_SETDISP_13_FP_PE (0x03)
+#define HX8369_CMD_SETDISP_14_RTN_PE (0x00)
+#define HX8369_CMD_SETDISP_15_GON (0x01)
+
+#define HX8369_CMD_SETCYC (0xB4)
+#define HX8369_CMD_SETCYC_LEN (6)
+#define HX8369_CMD_SETCYC_PARAM_1 (0x5f1d00)
+#define HX8369_CMD_SETCYC_PARAM_2 (0x060e)
+
+#define HX8369_CMD_SETGIP (0xD5)
+#define HX8369_CMD_SETGIP_LEN (27)
+#define HX8369_CMD_SETGIP_PARAM_1 (0x030400)
+#define HX8369_CMD_SETGIP_PARAM_2 (0x1c050100)
+#define HX8369_CMD_SETGIP_PARAM_3 (0x00030170)
+#define HX8369_CMD_SETGIP_PARAM_4 (0x51064000)
+#define HX8369_CMD_SETGIP_PARAM_5 (0x41000007)
+#define HX8369_CMD_SETGIP_PARAM_6 (0x07075006)
+#define HX8369_CMD_SETGIP_PARAM_7 (0x040f)
+
+#define HX8369_CMD_SETPOWER (0xB1)
+#define HX8369_CMD_SETPOWER_LEN (20)
+#define HX8369_CMD_SETPOWER_PARAM_1 (0x340001)
+#define HX8369_CMD_SETPOWER_PARAM_2 (0x0f0f0006)
+#define HX8369_CMD_SETPOWER_PARAM_3 (0x3f3f322a)
+#define HX8369_CMD_SETPOWER_PARAM_4 (0xe6013a07)
+#define HX8369_CMD_SETPOWER_PARAM_5 (0xe6e6e6e6)
+
+#define HX8369_CMD_SETVCOM (0xB6)
+#define HX8369_CMD_SETVCOM_LEN (3)
+#define HX8369_CMD_SETVCOM_PARAM_1 (0x5656)
+
+#define HX8369_CMD_SETPANEL (0xCC)
+#define HX8369_CMD_SETPANEL_PARAM_1 (0x02)
+
+#define HX8369_CMD_SETGAMMA (0xE0)
+#define HX8369_CMD_SETGAMMA_LEN (35)
+#define HX8369_CMD_SETGAMMA_PARAM_1 (0x221d00)
+#define HX8369_CMD_SETGAMMA_PARAM_2 (0x2e3f3d38)
+#define HX8369_CMD_SETGAMMA_PARAM_3 (0x0f0d064a)
+#define HX8369_CMD_SETGAMMA_PARAM_4 (0x16131513)
+#define HX8369_CMD_SETGAMMA_PARAM_5 (0x1d001910)
+#define HX8369_CMD_SETGAMMA_PARAM_6 (0x3f3d3822)
+#define HX8369_CMD_SETGAMMA_PARAM_7 (0x0d064a2e)
+#define HX8369_CMD_SETGAMMA_PARAM_8 (0x1315130f)
+#define HX8369_CMD_SETGAMMA_PARAM_9 (0x191016)
+
+#define HX8369_CMD_SETMIPI (0xBA)
+#define HX8369_CMD_SETMIPI_LEN (14)
+#define HX8369_CMD_SETMIPI_PARAM_1 (0xc6a000)
+#define HX8369_CMD_SETMIPI_PARAM_2 (0x10000a00)
+#define HX8369_CMD_SETMIPI_ONELANE (0x10 << 24)
+#define HX8369_CMD_SETMIPI_TWOLANE (0x11 << 24)
+#define HX8369_CMD_SETMIPI_PARAM_3 (0x00026f30)
+#define HX8369_CMD_SETMIPI_PARAM_4 (0x4018)
+
+#define HX8369_CMD_SETPIXEL_FMT (0x3A)
+#define HX8369_CMD_SETPIXEL_FMT_24BPP (0x77)
+#define HX8369_CMD_SETPIXEL_FMT_18BPP (0x66)
+#define HX8369_CMD_SETPIXEL_FMT_16BPP (0x55)
+
+#define HX8369_CMD_SETCLUMN_ADDR (0x2A)
+#define HX8369_CMD_SETCLUMN_ADDR_LEN (5)
+#define HX8369_CMD_SETCLUMN_ADDR_PARAM_1 (0xdf0000)
+#define HX8369_CMD_SETCLUMN_ADDR_PARAM_2 (0x01)
+
+#define HX8369_CMD_SETPAGE_ADDR (0x2B)
+#define HX8369_CMD_SETPAGE_ADDR_LEN (5)
+#define HX8369_CMD_SETPAGE_ADDR_PARAM_1 (0x1f0000)
+#define HX8369_CMD_SETPAGE_ADDR_PARAM_2 (0x03)
+
+#define HX8369_CMD_WRT_DISP_BRIGHT (0x51)
+#define HX8369_CMD_WRT_DISP_BRIGHT_PARAM_1 (0xFF)
+
+#define HX8369_CMD_WRT_CABC_MIN_BRIGHT (0x5E)
+#define HX8369_CMD_WRT_CABC_MIN_BRIGHT_PARAM_1 (0x20)
+
+#define HX8369_CMD_WRT_CABC_CTRL (0x55)
+#define HX8369_CMD_WRT_CABC_CTRL_PARAM_1 (0x1)
+
+#define HX8369_CMD_WRT_CTRL_DISP (0x53)
+#define HX8369_CMD_WRT_CTRL_DISP_PARAM_1 (0x24)
+
+#define CHECK_RETCODE(ret) \
+do { \
+ if (ret < 0) { \
+ dev_err(&mipi_dsi->pdev->dev, \
+ "%s ERR: ret:%d, line:%d.\n", \
+ __func__, ret, __LINE__); \
+ return ret; \
+ } \
+} while (0)
+
+static int hx8369bl_brightness;
+static int mipid_init_backlight(struct mipi_dsi_info *mipi_dsi);
+
+static struct fb_videomode truly_lcd_modedb[] = {
+ {
+ "TRULY-WVGA", 64, 480, 800, 37880,
+ 8, 8,
+ 6, 6,
+ 8, 6,
+ FB_SYNC_OE_LOW_ACT,
+ FB_VMODE_NONINTERLACED,
+ 0,
+ },
+};
+
+static struct mipi_lcd_config lcd_config = {
+ .virtual_ch = 0x0,
+ .data_lane_num = HX8369_TWO_DATA_LANE,
+ .max_phy_clk = HX8369_MAX_DPHY_CLK,
+ .dpi_fmt = MIPI_RGB888,
+};
+void mipid_hx8369_get_lcd_videomode(struct fb_videomode **mode, int *size,
+ struct mipi_lcd_config **data)
+{
+ *mode = &truly_lcd_modedb[0];
+ *size = ARRAY_SIZE(truly_lcd_modedb);
+ *data = &lcd_config;
+}
+
+int mipid_hx8369_lcd_setup(struct mipi_dsi_info *mipi_dsi)
+{
+ u32 buf[DSI_CMD_BUF_MAXSIZE];
+ int err;
+
+ dev_dbg(&mipi_dsi->pdev->dev, "MIPI DSI LCD setup.\n");
+ buf[0] = HX8369_CMD_SETEXTC | (HX8369_CMD_SETEXTC_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE,
+ buf, HX8369_CMD_SETEXTC_LEN);
+ CHECK_RETCODE(err);
+ buf[0] = MIPI_DSI_MAX_RET_PACK_SIZE;
+ err = mipi_dsi_pkt_write(mipi_dsi,
+ MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
+ buf, 0);
+ CHECK_RETCODE(err);
+ buf[0] = HX8369_CMD_GETHXID;
+ err = mipi_dsi_pkt_read(mipi_dsi,
+ MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM,
+ buf, HX8369_CMD_GETHXID_LEN);
+ if (!err && ((buf[0] & HX8369_ID_MASK) == HX8369_ID)) {
+ dev_info(&mipi_dsi->pdev->dev,
+ "MIPI DSI LCD ID:0x%x.\n", buf[0]);
+ } else {
+ dev_err(&mipi_dsi->pdev->dev,
+ "mipi_dsi_pkt_read err:%d, data:0x%x.\n",
+ err, buf[0]);
+ dev_info(&mipi_dsi->pdev->dev,
+ "MIPI DSI LCD not detected!\n");
+ return err;
+ }
+
+ /* set LCD resolution as 480RGBx800, DPI interface,
+ * display operation mode: RGB data bypass GRAM mode.
+ */
+ buf[0] = HX8369_CMD_SETDISP | (HX8369_CMD_SETDISP_1_HALT << 8) |
+ (HX8369_CMD_SETDISP_2_RES_MODE << 16) |
+ (HX8369_CMD_SETDISP_3_BP << 24);
+ buf[1] = HX8369_CMD_SETDISP_4_FP | (HX8369_CMD_SETDISP_5_SAP << 8) |
+ (HX8369_CMD_SETDISP_6_GENON << 16) |
+ (HX8369_CMD_SETDISP_7_GENOFF << 24);
+ buf[2] = HX8369_CMD_SETDISP_8_RTN | (HX8369_CMD_SETDISP_9_TEI << 8) |
+ (HX8369_CMD_SETDISP_10_TEP_UP << 16) |
+ (HX8369_CMD_SETDISP_11_TEP_LOW << 24);
+ buf[3] = HX8369_CMD_SETDISP_12_BP_PE |
+ (HX8369_CMD_SETDISP_13_FP_PE << 8) |
+ (HX8369_CMD_SETDISP_14_RTN_PE << 16) |
+ (HX8369_CMD_SETDISP_15_GON << 24);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE,
+ buf, HX8369_CMD_SETDISP_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set display waveform cycle */
+ buf[0] = HX8369_CMD_SETCYC | (HX8369_CMD_SETCYC_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETCYC_PARAM_2;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE,
+ buf, HX8369_CMD_SETCYC_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set GIP timing output control */
+ buf[0] = HX8369_CMD_SETGIP | (HX8369_CMD_SETGIP_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETGIP_PARAM_2;
+ buf[2] = HX8369_CMD_SETGIP_PARAM_3;
+ buf[3] = HX8369_CMD_SETGIP_PARAM_4;
+ buf[4] = HX8369_CMD_SETGIP_PARAM_5;
+ buf[5] = HX8369_CMD_SETGIP_PARAM_6;
+ buf[6] = HX8369_CMD_SETGIP_PARAM_7;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf,
+ HX8369_CMD_SETGIP_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set power: standby, DC etc. */
+ buf[0] = HX8369_CMD_SETPOWER | (HX8369_CMD_SETPOWER_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETPOWER_PARAM_2;
+ buf[2] = HX8369_CMD_SETPOWER_PARAM_3;
+ buf[3] = HX8369_CMD_SETPOWER_PARAM_4;
+ buf[4] = HX8369_CMD_SETPOWER_PARAM_5;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf,
+ HX8369_CMD_SETPOWER_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set VCOM voltage. */
+ buf[0] = HX8369_CMD_SETVCOM | (HX8369_CMD_SETVCOM_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf,
+ HX8369_CMD_SETVCOM_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set Panel: BGR/RGB or Inversion. */
+ buf[0] = HX8369_CMD_SETPANEL | (HX8369_CMD_SETPANEL_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi,
+ MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM, buf, 0);
+ CHECK_RETCODE(err);
+
+ /* Set gamma curve related setting */
+ buf[0] = HX8369_CMD_SETGAMMA | (HX8369_CMD_SETGAMMA_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETGAMMA_PARAM_2;
+ buf[2] = HX8369_CMD_SETGAMMA_PARAM_3;
+ buf[3] = HX8369_CMD_SETGAMMA_PARAM_4;
+ buf[4] = HX8369_CMD_SETGAMMA_PARAM_5;
+ buf[5] = HX8369_CMD_SETGAMMA_PARAM_6;
+ buf[7] = HX8369_CMD_SETGAMMA_PARAM_7;
+ buf[7] = HX8369_CMD_SETGAMMA_PARAM_8;
+ buf[8] = HX8369_CMD_SETGAMMA_PARAM_9;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf,
+ HX8369_CMD_SETGAMMA_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set MIPI: DPHYCMD & DSICMD, data lane number */
+ buf[0] = HX8369_CMD_SETMIPI | (HX8369_CMD_SETMIPI_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETMIPI_PARAM_2;
+ buf[2] = HX8369_CMD_SETMIPI_PARAM_3;
+ if (lcd_config.data_lane_num == HX8369_ONE_DATA_LANE)
+ buf[2] |= HX8369_CMD_SETMIPI_ONELANE;
+ else
+ buf[2] |= HX8369_CMD_SETMIPI_TWOLANE;
+ buf[3] = HX8369_CMD_SETMIPI_PARAM_4;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf,
+ HX8369_CMD_SETMIPI_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set pixel format:24bpp */
+ buf[0] = HX8369_CMD_SETPIXEL_FMT;
+ switch (lcd_config.dpi_fmt) {
+ case MIPI_RGB565_PACKED:
+ case MIPI_RGB565_LOOSELY:
+ case MIPI_RGB565_CONFIG3:
+ buf[0] |= (HX8369_CMD_SETPIXEL_FMT_16BPP << 8);
+ break;
+
+ case MIPI_RGB666_LOOSELY:
+ case MIPI_RGB666_PACKED:
+ buf[0] |= (HX8369_CMD_SETPIXEL_FMT_18BPP << 8);
+ break;
+
+ case MIPI_RGB888:
+ buf[0] |= (HX8369_CMD_SETPIXEL_FMT_24BPP << 8);
+ break;
+
+ default:
+ buf[0] |= (HX8369_CMD_SETPIXEL_FMT_24BPP << 8);
+ break;
+ }
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+
+ /* Set column address: 0~479 */
+ buf[0] = HX8369_CMD_SETCLUMN_ADDR |
+ (HX8369_CMD_SETCLUMN_ADDR_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETCLUMN_ADDR_PARAM_2;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE,
+ buf, HX8369_CMD_SETCLUMN_ADDR_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set page address: 0~799 */
+ buf[0] = HX8369_CMD_SETPAGE_ADDR |
+ (HX8369_CMD_SETPAGE_ADDR_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETPAGE_ADDR_PARAM_2;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE,
+ buf, HX8369_CMD_SETPAGE_ADDR_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set display brightness related */
+ buf[0] = HX8369_CMD_WRT_DISP_BRIGHT |
+ (HX8369_CMD_WRT_DISP_BRIGHT_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+
+ buf[0] = HX8369_CMD_WRT_CABC_CTRL |
+ (HX8369_CMD_WRT_CABC_CTRL_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+
+ buf[0] = HX8369_CMD_WRT_CTRL_DISP |
+ (HX8369_CMD_WRT_CTRL_DISP_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+
+ /* exit sleep mode and set display on */
+ buf[0] = MIPI_DCS_EXIT_SLEEP_MODE;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+ /* To allow time for the supply voltages
+ * and clock circuits to stabilize.
+ */
+ msleep(5);
+ buf[0] = MIPI_DCS_SET_DISPLAY_ON;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+
+ err = mipid_init_backlight(mipi_dsi);
+ return err;
+}
+
+static int mipid_bl_update_status(struct backlight_device *bl)
+{
+ u32 buf;
+ int brightness = bl->props.brightness;
+ struct mipi_dsi_info *mipi_dsi = bl_get_data(bl);
+
+ if (bl->props.power != FB_BLANK_UNBLANK ||
+ bl->props.fb_blank != FB_BLANK_UNBLANK)
+ brightness = 0;
+
+ buf = HX8369_CMD_WRT_DISP_BRIGHT |
+ ((brightness & HX8369BL_MAX_BRIGHT) << 8);
+ mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM,
+ &buf, 0);
+
+ hx8369bl_brightness = brightness & HX8369BL_MAX_BRIGHT;
+
+ dev_dbg(&bl->dev, "mipid backlight bringtness:%d.\n", brightness);
+ return 0;
+}
+
+static int mipid_bl_get_brightness(struct backlight_device *bl)
+{
+ return hx8369bl_brightness;
+}
+
+static int mipi_bl_check_fb(struct backlight_device *bl, struct fb_info *fbi)
+{
+ return 0;
+}
+
+static const struct backlight_ops mipid_lcd_bl_ops = {
+ .update_status = mipid_bl_update_status,
+ .get_brightness = mipid_bl_get_brightness,
+ .check_fb = mipi_bl_check_fb,
+};
+
+static int mipid_init_backlight(struct mipi_dsi_info *mipi_dsi)
+{
+ struct backlight_properties props;
+ struct backlight_device *bl;
+
+ if (mipi_dsi->bl) {
+ pr_debug("mipid backlight already init!\n");
+ return 0;
+ }
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.max_brightness = HX8369BL_MAX_BRIGHT;
+ props.type = BACKLIGHT_RAW;
+ bl = backlight_device_register("mipid-bl", &mipi_dsi->pdev->dev,
+ mipi_dsi, &mipid_lcd_bl_ops, &props);
+ if (IS_ERR(bl)) {
+ pr_err("error %ld on backlight register\n", PTR_ERR(bl));
+ return PTR_ERR(bl);
+ }
+ mipi_dsi->bl = bl;
+ bl->props.power = FB_BLANK_UNBLANK;
+ bl->props.fb_blank = FB_BLANK_UNBLANK;
+ bl->props.brightness = HX8369BL_DEF_BRIGHT;
+
+ mipid_bl_update_status(bl);
+ return 0;
+}
diff --git a/include/linux/mipi_dsi.h b/include/linux/mipi_dsi.h
new file mode 100644
index 000000000000..0bf33357ba90
--- /dev/null
+++ b/include/linux/mipi_dsi.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __INCLUDE_MIPI_DSI_H
+#define __INCLUDE_MIPI_DSI_H
+
+#define MIPI_DSI_VERSION (0x000)
+#define MIPI_DSI_PWR_UP (0x004)
+#define MIPI_DSI_CLKMGR_CFG (0x008)
+#define MIPI_DSI_DPI_CFG (0x00c)
+#define MIPI_DSI_DBI_CFG (0x010)
+#define MIPI_DSI_DBIS_CMDSIZE (0x014)
+#define MIPI_DSI_PCKHDL_CFG (0x018)
+#define MIPI_DSI_VID_MODE_CFG (0x01c)
+#define MIPI_DSI_VID_PKT_CFG (0x020)
+#define MIPI_DSI_CMD_MODE_CFG (0x024)
+#define MIPI_DSI_TMR_LINE_CFG (0x028)
+#define MIPI_DSI_VTIMING_CFG (0x02c)
+#define MIPI_DSI_PHY_TMR_CFG (0x030)
+#define MIPI_DSI_GEN_HDR (0x034)
+#define MIPI_DSI_GEN_PLD_DATA (0x038)
+#define MIPI_DSI_CMD_PKT_STATUS (0x03c)
+#define MIPI_DSI_TO_CNT_CFG (0x040)
+#define MIPI_DSI_ERROR_ST0 (0x044)
+#define MIPI_DSI_ERROR_ST1 (0x048)
+#define MIPI_DSI_ERROR_MSK0 (0x04c)
+#define MIPI_DSI_ERROR_MSK1 (0x050)
+#define MIPI_DSI_PHY_RSTZ (0x054)
+#define MIPI_DSI_PHY_IF_CFG (0x058)
+#define MIPI_DSI_PHY_IF_CTRL (0x05c)
+#define MIPI_DSI_PHY_STATUS (0x060)
+#define MIPI_DSI_PHY_TST_CTRL0 (0x064)
+#define MIPI_DSI_PHY_TST_CTRL1 (0x068)
+
+#define DSI_PWRUP_RESET (0x0 << 0)
+#define DSI_PWRUP_POWERUP (0x1 << 0)
+
+#define DSI_DPI_CFG_VID_SHIFT (0)
+#define DSI_DPI_CFG_VID_MASK (0x3)
+#define DSI_DPI_CFG_COLORCODE_SHIFT (2)
+#define DSI_DPI_CFG_COLORCODE_MASK (0x7)
+#define DSI_DPI_CFG_DATAEN_ACT_LOW (0x1 << 5)
+#define DSI_DPI_CFG_DATAEN_ACT_HIGH (0x0 << 5)
+#define DSI_DPI_CFG_VSYNC_ACT_LOW (0x1 << 6)
+#define DSI_DPI_CFG_VSYNC_ACT_HIGH (0x0 << 6)
+#define DSI_DPI_CFG_HSYNC_ACT_LOW (0x1 << 7)
+#define DSI_DPI_CFG_HSYNC_ACT_HIGH (0x0 << 7)
+#define DSI_DPI_CFG_SHUTD_ACT_LOW (0x1 << 8)
+#define DSI_DPI_CFG_SHUTD_ACT_HIGH (0x0 << 8)
+#define DSI_DPI_CFG_COLORMODE_ACT_LOW (0x1 << 9)
+#define DSI_DPI_CFG_COLORMODE_ACT_HIGH (0x0 << 9)
+#define DSI_DPI_CFG_EN18LOOSELY (0x1 << 10)
+
+#define DSI_PCKHDL_CFG_EN_EOTP_TX (0x1 << 0)
+#define DSI_PCKHDL_CFG_EN_EOTP_RX (0x1 << 1)
+#define DSI_PCKHDL_CFG_EN_BTA (0x1 << 2)
+#define DSI_PCKHDL_CFG_EN_ECC_RX (0x1 << 3)
+#define DSI_PCKHDL_CFG_EN_CRC_RX (0x1 << 4)
+#define DSI_PCKHDL_CFG_GEN_VID_RX_MASK (0x3)
+#define DSI_PCKHDL_CFG_GEN_VID_RX_SHIFT (5)
+
+#define DSI_VID_MODE_CFG_EN (0x1 << 0)
+#define DSI_VID_MODE_CFG_EN_BURSTMODE (0x3 << 1)
+#define DSI_VID_MODE_CFG_TYPE_MASK (0x3)
+#define DSI_VID_MODE_CFG_TYPE_SHIFT (1)
+#define DSI_VID_MODE_CFG_EN_LP_VSA (0x1 << 3)
+#define DSI_VID_MODE_CFG_EN_LP_VBP (0x1 << 4)
+#define DSI_VID_MODE_CFG_EN_LP_VFP (0x1 << 5)
+#define DSI_VID_MODE_CFG_EN_LP_VACT (0x1 << 6)
+#define DSI_VID_MODE_CFG_EN_LP_HBP (0x1 << 7)
+#define DSI_VID_MODE_CFG_EN_LP_HFP (0x1 << 8)
+#define DSI_VID_MODE_CFG_EN_MULTI_PKT (0x1 << 9)
+#define DSI_VID_MODE_CFG_EN_NULL_PKT (0x1 << 10)
+#define DSI_VID_MODE_CFG_EN_FRAME_ACK (0x1 << 11)
+#define DSI_VID_MODE_CFG_EN_LP_MODE (DSI_VID_MODE_CFG_EN_LP_VSA | \
+ DSI_VID_MODE_CFG_EN_LP_VBP | \
+ DSI_VID_MODE_CFG_EN_LP_VFP | \
+ DSI_VID_MODE_CFG_EN_LP_HFP | \
+ DSI_VID_MODE_CFG_EN_LP_HBP | \
+ DSI_VID_MODE_CFG_EN_LP_VACT)
+
+
+
+#define DSI_VID_PKT_CFG_VID_PKT_SZ_MASK (0x7ff)
+#define DSI_VID_PKT_CFG_VID_PKT_SZ_SHIFT (0)
+#define DSI_VID_PKT_CFG_NUM_CHUNKS_MASK (0x3ff)
+#define DSI_VID_PKT_CFG_NUM_CHUNKS_SHIFT (11)
+#define DSI_VID_PKT_CFG_NULL_PKT_SZ_MASK (0x3ff)
+#define DSI_VID_PKT_CFG_NULL_PKT_SZ_SHIFT (21)
+
+#define MIPI_DSI_CMD_MODE_CFG_EN_LOWPOWER (0x1FFF)
+#define MIPI_DSI_CMD_MODE_CFG_EN_CMD_MODE (0x1 << 0)
+
+#define DSI_TME_LINE_CFG_HSA_TIME_MASK (0x1ff)
+#define DSI_TME_LINE_CFG_HSA_TIME_SHIFT (0)
+#define DSI_TME_LINE_CFG_HBP_TIME_MASK (0x1ff)
+#define DSI_TME_LINE_CFG_HBP_TIME_SHIFT (9)
+#define DSI_TME_LINE_CFG_HLINE_TIME_MASK (0x3fff)
+#define DSI_TME_LINE_CFG_HLINE_TIME_SHIFT (18)
+
+#define DSI_VTIMING_CFG_VSA_LINES_MASK (0xf)
+#define DSI_VTIMING_CFG_VSA_LINES_SHIFT (0)
+#define DSI_VTIMING_CFG_VBP_LINES_MASK (0x3f)
+#define DSI_VTIMING_CFG_VBP_LINES_SHIFT (4)
+#define DSI_VTIMING_CFG_VFP_LINES_MASK (0x3f)
+#define DSI_VTIMING_CFG_VFP_LINES_SHIFT (10)
+#define DSI_VTIMING_CFG_V_ACT_LINES_MASK (0x7ff)
+#define DSI_VTIMING_CFG_V_ACT_LINES_SHIFT (16)
+
+#define DSI_PHY_TMR_CFG_BTA_TIME_MASK (0xfff)
+#define DSI_PHY_TMR_CFG_BTA_TIME_SHIFT (0)
+#define DSI_PHY_TMR_CFG_LP2HS_TIME_MASK (0xff)
+#define DSI_PHY_TMR_CFG_LP2HS_TIME_SHIFT (12)
+#define DSI_PHY_TMR_CFG_HS2LP_TIME_MASK (0xff)
+#define DSI_PHY_TMR_CFG_HS2LP_TIME_SHIFT (20)
+
+#define DSI_PHY_IF_CFG_N_LANES_MASK (0x3)
+#define DSI_PHY_IF_CFG_N_LANES_SHIFT (0)
+#define DSI_PHY_IF_CFG_WAIT_TIME_MASK (0xff)
+#define DSI_PHY_IF_CFG_WAIT_TIME_SHIFT (2)
+
+#define DSI_PHY_RSTZ_EN_CLK (0x1 << 2)
+#define DSI_PHY_RSTZ_DISABLE_RST (0x1 << 1)
+#define DSI_PHY_RSTZ_DISABLE_SHUTDOWN (0x1 << 0)
+#define DSI_PHY_RSTZ_RST (0x0)
+
+#define DSI_PHY_STATUS_LOCK (0x1 << 0)
+#define DSI_PHY_STATUS_STOPSTATE_CLK_LANE (0x1 << 2)
+
+#define DSI_GEN_HDR_TYPE_MASK (0xff)
+#define DSI_GEN_HDR_TYPE_SHIFT (0)
+#define DSI_GEN_HDR_DATA_MASK (0xffff)
+#define DSI_GEN_HDR_DATA_SHIFT (8)
+
+#define DSI_CMD_PKT_STATUS_GEN_CMD_EMPTY (0x1 << 0)
+#define DSI_CMD_PKT_STATUS_GEN_CMD_FULL (0x1 << 1)
+#define DSI_CMD_PKT_STATUS_GEN_PLD_W_EMPTY (0x1 << 2)
+#define DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL (0x1 << 3)
+#define DSI_CMD_PKT_STATUS_GEN_PLD_R_EMPTY (0x1 << 4)
+#define DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY (0x1 << 6)
+
+#define DSI_ERROR_MSK0_ALL_MASK (0x1fffff)
+#define DSI_ERROR_MSK1_ALL_MASK (0x3ffff)
+
+#define DSI_PHY_IF_CTRL_RESET (0x0)
+#define DSI_PHY_IF_CTRL_TX_REQ_CLK_HS (0x1 << 0)
+#define DSI_PHY_IF_CTRL_TX_REQ_CLK_ULPS (0x1 << 1)
+#define DSI_PHY_IF_CTRL_TX_EXIT_CLK_ULPS (0x1 << 2)
+#define DSI_PHY_IF_CTRL_TX_REQ_DATA_ULPS (0x1 << 3)
+#define DSI_PHY_IF_CTRL_TX_EXIT_DATA_ULPS (0x1 << 4)
+#define DSI_PHY_IF_CTRL_TX_TRIG_MASK (0xF)
+#define DSI_PHY_IF_CTRL_TX_TRIG_SHIFT (5)
+
+#define DSI_PHY_CLK_INIT_COMMAND (0x44)
+#define DSI_GEN_PLD_DATA_BUF_SIZE (0x4)
+#endif