diff options
author | Wayne Zou <b36644@freescale.com> | 2011-10-14 14:55:08 +0800 |
---|---|---|
committer | Jason Liu <r64343@freescale.com> | 2012-01-09 21:03:52 +0800 |
commit | 84a449f15240d95356a05e0b7c8aedf0c28976a4 (patch) | |
tree | 9634cce315ac15d54cff3a0a85cd493a7f069b6b /drivers/video/mxc | |
parent | 0fb6190db1c58938cd2697beaf28fdca6377b0e3 (diff) |
ENGR00159981-2 MIPI DSI: Add MIPI DSI driver and support TRULY WVGA LCD panel
MIPI DSI: Add MIPI DSI driver and support for TRULY WVGA LCD panel
Signed-off-by: Wayne Zou <b36644@freescale.com>
Diffstat (limited to 'drivers/video/mxc')
-rw-r--r-- | drivers/video/mxc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/video/mxc/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/mxc/mipi_dsi.c | 937 | ||||
-rw-r--r-- | drivers/video/mxc/mipi_dsi.h | 108 | ||||
-rw-r--r-- | drivers/video/mxc/mxcfb_hx8369_wvga.c | 384 |
5 files changed, 1440 insertions, 0 deletions
diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig index cec114a602d1..1c548d0d3b5f 100644 --- a/drivers/video/mxc/Kconfig +++ b/drivers/video/mxc/Kconfig @@ -34,6 +34,15 @@ 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 && FB_MXC_MIPI_DSI + config FB_MXC_CLAA_WVGA_SYNC_PANEL depends on FB_MXC_SYNC_PANEL tristate "CLAA WVGA Panel" diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile index 4fdb822105f1..debae230bb25 100644 --- a/drivers/video/mxc/Makefile +++ b/drivers/video/mxc/Makefile @@ -1,6 +1,8 @@ obj-$(CONFIG_FB_MXC_TVOUT_TVE) += tve.o obj-$(CONFIG_FB_MXC_SII902X) += mxcfb_sii902x.o 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_MODE_HELPERS) += mxc_edid.o mxc_dvi.o ifeq ($(CONFIG_ARCH_MX21)$(CONFIG_ARCH_MX27)$(CONFIG_ARCH_MX25),y) obj-$(CONFIG_FB_MXC_TVOUT) += fs453.o diff --git a/drivers/video/mxc/mipi_dsi.c b/drivers/video/mxc/mipi_dsi.c new file mode 100644 index 000000000000..8483041faae2 --- /dev/null +++ b/drivers/video/mxc/mipi_dsi.c @@ -0,0 +1,937 @@ +/* + * Copyright (C) 2011 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/mxcfb.h> +#include <linux/regulator/consumer.h> +#include <linux/backlight.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/fsl_devices.h> +#include <video/mipi_display.h> + +#include <mach/hardware.h> +#include <mach/clock.h> +#include <mach/mipi_dsi.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 MIPI_LCD_SLEEP_MODE_DELAY (120) + +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 dsi_power_on; + +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); + msleep(1); + dev_dbg(&mipi_dsi->pdev->dev, "\t\twrite_reg:0x%02x, val:0x%08x.\n", + reg, val); +} + +static void mipi_dsi_dump_registers(struct mipi_dsi_info *mipi_dsi) +{ + int i; + u32 val; + + for (i = MIPI_DSI_VERSION; i <= MIPI_DSI_PHY_TST_CTRL1; i += 4) + mipi_dsi_read_register(mipi_dsi, i, &val); +} + +int mipi_dsi_pkt_write(struct mipi_dsi_info *mipi_dsi, + u8 data_type, const u32 *buf, int len) +{ + u32 val; + u32 status; + int write_len = len; + + 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) + mipi_dsi_read_register(mipi_dsi, + MIPI_DSI_CMD_PKT_STATUS, &status); + } + /* write the remainder bytes */ + if (len > 0) { + 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) + 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) + 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)) + 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; + + if (!len) { + mipi_dbg("%s,%d: error!\n", __func__, __LINE__); + return -EINVAL; + } + + val = data_type | ((*buf & DSI_GEN_HDR_DATA_MASK) + << DSI_GEN_HDR_DATA_SHIFT); + mipi_dsi_write_register(mipi_dsi, MIPI_DSI_GEN_HDR, val); + + /* wait for entire response stroed in FIFO */ + 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) + 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 ((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 -EIO; + } +} + +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; + + 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) + mipi_dsi_read_register(mipi_dsi, MIPI_DSI_PHY_STATUS, &val); + while ((val & DSI_PHY_STATUS_STOPSTATE_CLK_LANE) != + DSI_PHY_STATUS_STOPSTATE_CLK_LANE) + 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_EOTP_TX | + DSI_PCKHDL_CFG_EN_EOTP_RX | + DSI_PCKHDL_CFG_EN_BTA | + DSI_PCKHDL_CFG_EN_ECC_RX | + DSI_PCKHDL_CFG_EN_CRC_RX; + + val |= ((lcd_config->virtual_ch + 1) & + DSI_PCKHDL_CFG_GEN_VID_RX_MASK) + << DSI_PCKHDL_CFG_GEN_VID_RX_SHIFT; + 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); + + 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); + dsi_power_on = 1; + } 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 void mipi_dsi_power_on(struct mipi_dsi_info *mipi_dsi) +{ + int err; + u32 val; + + if (!dsi_power_on) { + mipi_dsi_enable_controller(mipi_dsi, false); + 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); + } + dsi_power_on = 1; + /* 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); + + mipi_dsi_read_register(mipi_dsi, MIPI_DSI_VID_MODE_CFG, &val); + val |= DSI_VID_MODE_CFG_EN; + mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_MODE_CFG, val); + + mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL, + DSI_PHY_IF_CTRL_TX_REQ_CLK_HS); + + mipi_dsi_dump_registers(mipi_dsi); +} + +static void mipi_dsi_power_off(struct mipi_dsi_info *mipi_dsi) +{ + int err; + u32 val; + + if (dsi_power_on) { + 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_read_register(mipi_dsi, MIPI_DSI_VID_MODE_CFG, &val); + val &= ~DSI_VID_MODE_CFG_EN; + mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_MODE_CFG, val); + + 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"); + } + msleep(MIPI_LCD_SLEEP_MODE_DELAY); + + mipi_dsi_disable_controller(mipi_dsi); + dsi_power_on = 0; + } +} + +static int mipi_dsi_lcd_init(struct mipi_dsi_info *mipi_dsi) +{ + int err; + int size; + int i; + struct fb_videomode *mipi_lcd_modedb; + struct fb_videomode mode; + struct device *dev = &mipi_dsi->pdev->dev; + struct mxc_dispdrv_setting *setting; + + setting = mxc_dispdrv_getsetting(mipi_dsi->disp_mipi); + 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); + + mipi_dsi_enable_controller(mipi_dsi, true); + + err = mipi_dsi->lcd_callback->mipi_lcd_setup(mipi_dsi); + if (err < 0) { + dev_err(dev, "failed to init mipi lcd.\n"); + return err; + } + + mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL, + DSI_PHY_IF_CTRL_TX_REQ_CLK_HS); + + return 0; +} + +static int mipi_dsi_fb_event(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct mipi_dsi_info *mipi_dsi = + container_of(nb, struct mipi_dsi_info, nb); + struct fb_event *fbevent = data; + + switch (event) { + case FB_EVENT_FB_REGISTERED: + break; + + case FB_EVENT_BLANK: + if (*((int *)fbevent->data) == FB_BLANK_UNBLANK) + mipi_dsi_power_on(mipi_dsi); + else + mipi_dsi_power_off(mipi_dsi); + break; + + default: + break; + } + + return 0; +} + +static int mipi_dsi_disp_init(struct mxc_dispdrv_entry *disp) +{ + int err; + char dphy_clk[] = "mipi_pllref_clk"; + struct resource *res; + struct resource *res_irq; + struct device *dev; + struct mipi_dsi_info *mipi_dsi; + struct mxc_dispdrv_setting *setting; + struct mipi_dsi_platform_data *pdata; + + mipi_dsi = mxc_dispdrv_getdata(disp); + setting = mxc_dispdrv_getsetting(disp); + if (IS_ERR(mipi_dsi) || IS_ERR(setting)) { + pr_err("failed to get dispdrv data\n"); + return -EINVAL; + } + dev = &mipi_dsi->pdev->dev; + pdata = dev->platform_data; + if (!pdata) { + dev_err(dev, "No platform_data available\n"); + return -EINVAL; + } + + mipi_dsi->lcd_panel = kstrdup(pdata->lcd_panel, GFP_KERNEL); + if (!mipi_dsi->lcd_panel) + return -ENOMEM; + + 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; + } + + res = platform_get_resource(mipi_dsi->pdev, IORESOURCE_MEM, 0); + if (!res) { + mipi_dbg("%s,%d: error!\n", __func__, __LINE__); + return -ENODEV; + } + + res = request_mem_region(res->start, resource_size(res), + mipi_dsi->pdev->name); + if (!res) { + mipi_dbg("%s,%d: error!\n", __func__, __LINE__); + return -EBUSY; + } + mipi_dsi->mmio_base = ioremap(res->start, resource_size(res)); + if (!mipi_dsi->mmio_base) { + dev_err(dev, "Cannot map mipi dsi registers\n"); + err = -EIO; + goto err_ioremap; + } + + res_irq = platform_get_resource(mipi_dsi->pdev, IORESOURCE_IRQ, 0); + if (!res_irq) { + dev_err(dev, "failed to acquire irq resource\n"); + err = -ENODEV; + goto err_get_irq; + } + mipi_dsi->irq = res_irq->start; + + mipi_dsi->dphy_clk = clk_get(dev, dphy_clk); + if (IS_ERR(mipi_dsi->dphy_clk)) { + dev_err(dev, "failed to get dphy pll_ref_clk\n"); + err = PTR_ERR(mipi_dsi->dphy_clk); + goto err_clk; + } + err = clk_enable(mipi_dsi->dphy_clk); + if (err) + mipi_dbg("%s,%d: error!\n", __func__, __LINE__); + + dev_dbg(dev, "got resources: regs %p, irq:%d\n", + mipi_dsi->mmio_base, mipi_dsi->irq); + + if (pdata->io_regulator) { + mipi_dsi->io_regulator = regulator_get(dev, + pdata->io_regulator); + if (IS_ERR(mipi_dsi->io_regulator)) { + dev_err(dev, "failed to get io_regulator\n"); + err = PTR_ERR(mipi_dsi->io_regulator); + goto err_ioreg; + } + err = regulator_set_voltage(mipi_dsi->io_regulator, + pdata->io_volt, + pdata->io_volt); + if (err < 0) { + dev_err(dev, "failed to set io_regulator voltage\n"); + goto err_corereg; + } + err = regulator_enable(mipi_dsi->io_regulator); + if (err < 0) { + dev_err(dev, "failed to enable io_regulator voltage\n"); + goto err_corereg; + } + } + if (pdata->core_regulator) { + mipi_dsi->core_regulator = regulator_get(dev, + pdata->core_regulator); + if (IS_ERR(mipi_dsi->core_regulator)) { + dev_err(dev, "failed to get core_regulator\n"); + err = PTR_ERR(mipi_dsi->core_regulator); + goto err_corereg; + } + err = regulator_set_voltage(mipi_dsi->core_regulator, + pdata->core_volt, + pdata->core_volt); + if (err < 0) { + dev_err(dev, "failed to set core_regulator voltage\n"); + goto err_analogreg; + } + err = regulator_enable(mipi_dsi->core_regulator); + if (err < 0) { + dev_err(dev, + "failed to enable core_regulator voltage\n"); + goto err_analogreg; + } + } + if (pdata->analog_regulator) { + mipi_dsi->analog_regulator = + regulator_get(dev, pdata->analog_regulator); + if (IS_ERR(mipi_dsi->analog_regulator)) { + dev_err(dev, "failed to get analog_regulator\n"); + err = PTR_ERR(mipi_dsi->analog_regulator); + goto err_analogreg; + } + err = regulator_set_voltage(mipi_dsi->analog_regulator, + pdata->analog_volt, + pdata->analog_volt); + if (err < 0) { + dev_err(dev, + "failed to set analog_regulator voltage\n"); + goto err_pdata_init; + } + err = regulator_enable(mipi_dsi->analog_regulator); + if (err < 0) { + dev_err(dev, + "failed to enable analog_regulator voltage\n"); + goto err_pdata_init; + } + } + if (pdata->lcd_power) + pdata->lcd_power(true); + if (pdata->backlight_power) + pdata->backlight_power(true); + + if (pdata->init) { + err = pdata->init(mipi_dsi->pdev); + if (err < 0) { + dev_err(dev, "failed to do pdata->init()\n"); + goto err_pdata_init; + } + } + if (pdata->reset) + pdata->reset(); + + mipi_dsi->nb.notifier_call = mipi_dsi_fb_event; + err = fb_register_client(&mipi_dsi->nb); + if (err < 0) { + dev_err(dev, "failed to register fb notifier\n"); + goto err_reg_nb; + } + mipi_dsi->ipu_id = pdata->ipu_id; + mipi_dsi->disp_id = pdata->disp_id; + mipi_dsi->reset = pdata->reset; + mipi_dsi->lcd_power = pdata->lcd_power; + mipi_dsi->backlight_power = pdata->backlight_power; + + /* ipu selected by platform data setting */ + setting->dev_id = pdata->ipu_id; + setting->disp_id = pdata->disp_id; + + err = request_irq(mipi_dsi->irq, mipi_dsi_irq_handler, + 0, "mipi_dsi", mipi_dsi); + if (err) { + dev_err(dev, "failed to request irq\n"); + err = -EBUSY; + goto err_req_irq; + } + + err = mipi_dsi_lcd_init(mipi_dsi); + if (err < 0) { + dev_err(dev, "failed to init mipi dsi lcd\n"); + goto err_dsi_lcd; + } + + dev_dbg(dev, "MIPI DSI dispdrv inited!\n"); + return 0; + +err_dsi_lcd: + free_irq(mipi_dsi->irq, mipi_dsi); +err_req_irq: + fb_unregister_client(&mipi_dsi->nb); +err_reg_nb: + if (pdata->exit) + pdata->exit(mipi_dsi->pdev); +err_pdata_init: + /* cannot disable analog/io/core regulator, maybe others use it, + * according to board design + */ + if (mipi_dsi->analog_regulator) + regulator_put(mipi_dsi->analog_regulator); +err_analogreg: + if (mipi_dsi->core_regulator) + regulator_put(mipi_dsi->core_regulator); +err_corereg: + if (mipi_dsi->io_regulator) + regulator_put(mipi_dsi->io_regulator); +err_ioreg: + clk_disable(mipi_dsi->dphy_clk); + clk_put(mipi_dsi->dphy_clk); +err_clk: +err_get_irq: + iounmap(mipi_dsi->mmio_base); +err_ioremap: + release_mem_region(res->start, resource_size(res)); + + return err; +} + +static void mipi_dsi_disp_deinit(struct mxc_dispdrv_entry *disp) +{ + struct mipi_dsi_info *mipi_dsi; + struct resource *res; + + mipi_dsi = mxc_dispdrv_getdata(disp); + res = platform_get_resource(mipi_dsi->pdev, IORESOURCE_MEM, 0); + + disable_irq(mipi_dsi->irq); + free_irq(mipi_dsi->irq, mipi_dsi); + mipi_dsi_power_off(mipi_dsi); + fb_unregister_client(&mipi_dsi->nb); + if (mipi_dsi->bl) + backlight_device_unregister(mipi_dsi->bl); + if (mipi_dsi->analog_regulator) + regulator_put(mipi_dsi->analog_regulator); + if (mipi_dsi->core_regulator) + regulator_put(mipi_dsi->core_regulator); + if (mipi_dsi->io_regulator) + regulator_put(mipi_dsi->io_regulator); + clk_disable(mipi_dsi->dphy_clk); + clk_put(mipi_dsi->dphy_clk); + iounmap(mipi_dsi->mmio_base); + release_mem_region(res->start, resource_size(res)); +} + +static struct mxc_dispdrv_driver mipi_dsi_drv = { + .name = DISPDRV_MIPI, + .init = mipi_dsi_disp_init, + .deinit = mipi_dsi_disp_deinit, +}; + +/** + * 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) +{ + int ret; + struct mipi_dsi_info *mipi_dsi; + + mipi_dsi = kzalloc(sizeof(struct mipi_dsi_info), GFP_KERNEL); + if (!mipi_dsi) { + ret = -ENOMEM; + goto alloc_failed; + } + + mipi_dsi->pdev = pdev; + mipi_dsi->disp_mipi = mxc_dispdrv_register(&mipi_dsi_drv); + if (IS_ERR(mipi_dsi->disp_mipi)) { + mipi_dbg("%s,%d: error!\n", __func__, __LINE__); + ret = -ENOMEM; + goto register_failed; + } + + 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 0; + +register_failed: + kfree(mipi_dsi); +alloc_failed: + 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); + if (mipi_dsi->lcd_power) + mipi_dsi->lcd_power(false); + if (mipi_dsi->backlight_power) + mipi_dsi->backlight_power(false); +} + +static int __devexit mipi_dsi_remove(struct platform_device *pdev) +{ + struct mipi_dsi_info *mipi_dsi = dev_get_drvdata(&pdev->dev); + + mxc_dispdrv_unregister(mipi_dsi->disp_mipi); + kfree(mipi_dsi); + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static struct platform_driver mipi_dsi_driver = { + .driver = { + .name = "mxc_mipi_dsi", + }, + .probe = mipi_dsi_probe, + .remove = __devexit_p(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..a70a75559dac --- /dev/null +++ b/drivers/video/mxc/mipi_dsi.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2011 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__ + +#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; +}; + +/* driver private data */ +struct mipi_dsi_info { + int ipu_id; + int disp_id; + char *lcd_panel; + int irq; + u32 dphy_pll_config; + struct clk *dphy_clk; + void __iomem *mmio_base; + struct platform_device *pdev; + struct mxc_dispdrv_entry *disp_mipi; + struct notifier_block nb; + struct fb_videomode *mode; + struct mipi_lcd_config *lcd_config; + /* board related power control */ + struct backlight_device *bl; + struct regulator *io_regulator; + struct regulator *core_regulator; + struct regulator *analog_regulator; + int io_volt; + int core_volt; + int analog_volt; + void (*reset) (void); + void (*lcd_power)(int); + void (*backlight_power)(int); + /* 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..f9e5de4a4097 --- /dev/null +++ b/drivers/video/mxc/mxcfb_hx8369_wvga.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2011 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/mxcfb.h> +#include <linux/backlight.h> +#include <video/mipi_display.h> + +#include <mach/hardware.h> +#include <mach/clock.h> +#include <mach/mipi_dsi.h> + +#include "mipi_dsi.h" + +#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_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) +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; + + buf[0] = HX8369_CMD_SETEXTC | (HX8369_CMD_SETEXTC_PARAM_1 << 8); + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, + buf, HX8369_CMD_SETEXTC_LEN); + /* 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); + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, + buf, HX8369_CMD_SETDISP_LEN); + /* Set display waveform cycle */ + buf[0] = HX8369_CMD_SETCYC | (HX8369_CMD_SETCYC_PARAM_1 << 8); + buf[1] = HX8369_CMD_SETCYC_PARAM_2; + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, + buf, HX8369_CMD_SETCYC_LEN); + + /* 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; + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf, + HX8369_CMD_SETGIP_LEN); + + /* 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; + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf, + HX8369_CMD_SETPOWER_LEN); + + /* Set VCOM voltage. */ + buf[0] = HX8369_CMD_SETVCOM | (HX8369_CMD_SETVCOM_PARAM_1 << 8); + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf, + HX8369_CMD_SETVCOM_LEN); + + /* Set Panel: BGR/RGB or Inversion. */ + buf[0] = HX8369_CMD_SETPANEL | (HX8369_CMD_SETPANEL_PARAM_1 << 8); + mipi_dsi_pkt_write(mipi_dsi, + MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM, buf, 0); + + /* 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; + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf, + HX8369_CMD_SETGAMMA_LEN); + + /* 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; + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf, + HX8369_CMD_SETMIPI_LEN); + + /* 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; + } + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM, + buf, 0); + + /* 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; + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, + buf, HX8369_CMD_SETCLUMN_ADDR_LEN); + + /* 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; + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, + buf, HX8369_CMD_SETPAGE_ADDR_LEN); + + /* Set display brightness related */ + buf[0] = HX8369_CMD_WRT_DISP_BRIGHT | + (HX8369_CMD_WRT_DISP_BRIGHT_PARAM_1 << 8); + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM, + buf, 0); + + buf[0] = HX8369_CMD_WRT_CABC_CTRL | + (HX8369_CMD_WRT_CABC_CTRL_PARAM_1 << 8); + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM, + buf, 0); + + buf[0] = HX8369_CMD_WRT_CTRL_DISP | + (HX8369_CMD_WRT_CTRL_DISP_PARAM_1 << 8); + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM, + buf, 0); + + /* exit sleep mode and set display on */ + buf[0] = MIPI_DCS_EXIT_SLEEP_MODE; + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM, + buf, 0); + + buf[0] = MIPI_DCS_SET_DISPLAY_ON; + mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM, + buf, 0); + + 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 const struct backlight_ops mipid_lcd_bl_ops = { + .update_status = mipid_bl_update_status, + .get_brightness = mipid_bl_get_brightness, +}; + +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; + 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; +} |