diff options
-rw-r--r-- | Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt | 28 | ||||
-rw-r--r-- | drivers/video/mxc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/video/mxc/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/mxc/mipi_dsi.c | 953 | ||||
-rw-r--r-- | drivers/video/mxc/mipi_dsi.h | 112 | ||||
-rw-r--r-- | drivers/video/mxc/mxcfb_hx8369_wvga.c | 449 | ||||
-rw-r--r-- | include/linux/mipi_dsi.h | 171 |
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 = <®_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 |