aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaojian Zhuang <haojian.zhuang@linaro.org>2013-04-04 16:14:57 +0800
committerHaojian Zhuang <haojian.zhuang@linaro.org>2013-04-04 16:14:57 +0800
commit59dbe89dc4ff20001ec8288b4706184a2a613fe4 (patch)
tree82bcd0fb4cc34210fb76a293b58d0668583f5cfb
parentb1c56292a748d7f4748c480bf89a8d0941e92ac0 (diff)
fb: append hi3620 driver
Add Hisilicon Hi3620 framebuffer driver. Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
-rw-r--r--drivers/video/Kconfig1
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/hisilicon/Kconfig9
-rw-r--r--drivers/video/hisilicon/Makefile2
-rw-r--r--drivers/video/hisilicon/hi3620_dsi.c353
-rw-r--r--drivers/video/hisilicon/hi3620_fb.c650
-rw-r--r--drivers/video/hisilicon/hi3620_fb.h125
-rw-r--r--include/linux/platform_data/hi3620-dsi.h32
8 files changed, 1173 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 807c7fa689fa..2343912499d0 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2443,6 +2443,7 @@ config FB_PUV3_UNIGFX
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
source "drivers/video/exynos/Kconfig"
+source "drivers/video/hisilicon/Kconfig"
source "drivers/video/backlight/Kconfig"
if VT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index f592f3b32ec7..b322ebb7bc76 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -161,6 +161,7 @@ obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
obj-$(CONFIG_FB_MXS) += mxsfb.o
+obj-$(CONFIG_FB_HI3620) += hisilicon/
obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o
# the test framebuffer is last
diff --git a/drivers/video/hisilicon/Kconfig b/drivers/video/hisilicon/Kconfig
new file mode 100644
index 000000000000..a683760d2185
--- /dev/null
+++ b/drivers/video/hisilicon/Kconfig
@@ -0,0 +1,9 @@
+config FB_HI3620
+ tristate "Hisilicon Hi3620 Frame Buffer support"
+ depends on FB && ARCH_HS
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This is the frame buffer device driver for the Hisilicon Hi3620 LCD
+ controller.
diff --git a/drivers/video/hisilicon/Makefile b/drivers/video/hisilicon/Makefile
new file mode 100644
index 000000000000..1f4b79befe51
--- /dev/null
+++ b/drivers/video/hisilicon/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_FB_HI3620) += hi3620_fb.o
+obj-y += hi3620_dsi.o
diff --git a/drivers/video/hisilicon/hi3620_dsi.c b/drivers/video/hisilicon/hi3620_dsi.c
new file mode 100644
index 000000000000..af3dd68abd81
--- /dev/null
+++ b/drivers/video/hisilicon/hi3620_dsi.c
@@ -0,0 +1,353 @@
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/fb.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/platform_data/hi3620-dsi.h>
+#include "hi3620_fb.h"
+
+/* DSI PHY internal register */
+#define DSI_PHY_CP_CURRENT 0x11
+#define DSI_PHY_LPF_CTRL 0x12
+#define DSI_PHY_PLL_UNLOCK_FILTER 0x16
+#define DSI_PHY_N_PLL 0x17
+#define DSI_PHY_M_PLL 0x18
+#define DSI_PHY_FACTOR 0x19
+#define DSI_PHY_HS_FREQ_RANGE 0x44
+
+#define PHY_ADDR (1 << 16)
+
+#define DTYPE_GEN_WRITE_HEAD 0x03
+#define DTYPE_GEN_WRITE_PAYLOAD 0x29
+
+struct phy_timing {
+ int dsi2x; /* MHz */
+ int lp2hs;
+ int hs2lp;
+ int hsfreq;
+};
+
+static void hi3620_mipi_reset(void __iomem *reg_base)
+{
+ writel_relaxed(0, reg_base + DSI_PWR_UP);
+}
+
+static void hi3620_mipi_unreset(void __iomem *reg_base)
+{
+ writel_relaxed(1, reg_base + DSI_PWR_UP);
+}
+
+static void hi3620_mipi_set_highspeed(void __iomem *reg_base)
+{
+ unsigned int data;
+
+ /* enable high speed clock for PHY */
+ data = readl_relaxed(reg_base + DSI_PHY_IF_CTRL);
+ data |= (1 << 0);
+ writel_relaxed(data, reg_base + DSI_PHY_IF_CTRL);
+}
+
+static void hi3620_mipi_unset_highspeed(void __iomem *reg_base)
+{
+ unsigned int data;
+
+ /* disable high speed clock for PHY */
+ data = readl_relaxed(reg_base + DSI_PHY_IF_CTRL);
+ data &= ~(1 << 0);
+ writel_relaxed(data, reg_base + DSI_PHY_IF_CTRL);
+}
+
+/* Switch to CMD mode with Low Power mode */
+static void hi3620_mipi_switch_cmd_mode(void __iomem *reg_base)
+{
+ unsigned int data;
+
+ /* disable VIDEO mode */
+ data = readl_relaxed(reg_base + DSI_VID_MODE_CFG);
+ data &= ~(1 << 0);
+ writel_relaxed(data, reg_base + DSI_VID_MODE_CFG);
+ data = readl_relaxed(reg_base + DSI_CMD_MODE_CFG);
+ data |= ((1 << 13) - 1);
+ writel_relaxed(data, reg_base + DSI_CMD_MODE_CFG);
+}
+
+/* Switch to VIDEO mode */
+static void hi3620_mipi_switch_video_mode(void __iomem *reg_base)
+{
+ unsigned int data;
+
+ /* disable CMD mode */
+ data = readl_relaxed(reg_base + DSI_CMD_MODE_CFG);
+ data &= ~(1 << 0);
+ writel_relaxed(data, reg_base + DSI_CMD_MODE_CFG);
+ /* enable VIDEO mode */
+ data = readl_relaxed(reg_base + DSI_VID_MODE_CFG);
+ data |= (1 << 0);
+ writel_relaxed(data, reg_base + DSI_VID_MODE_CFG);
+}
+
+static void hi3620_mipi_enable_phy(void __iomem *reg_base)
+{
+ writel_relaxed(0x7, reg_base + DSI_PHY_RSTZ);
+}
+
+static void hi3620_mipi_disable_phy(void __iomem *reg_base)
+{
+ writel_relaxed(0, reg_base + DSI_PHY_RSTZ);
+}
+
+#if 0
+static void hi3620_mipi_setup_cmd_mode(void __iomem *reg_base)
+{
+ /* enable CMD halt & disable VIDEO halt & discret WMS */
+ writel_relaxed(1, reg_base + DSI_CMD_MOD_CTRL);
+ /* enable TE for CMD mode */
+ writel_relaxed(0x4001, reg_base + DSI_TE_CTRL);
+ /* send data only when VSync is detected */
+ writel_relaxed(0, reg_base + DSI_TE_HS_NUM);
+ /* HSync width */
+ writel_relaxed(0x1001, reg_base + DSI_TE_HS_WD);
+ /* VSync width > Hsync width */
+ writel_relaxed(0x8002, reg_base + DSI_TE_VS_WD);
+}
+
+static void hi3620_mipi_setup_video_mode(struct hi3620fb_info *info)
+{
+ struct fb_info *fb = info->fb;
+ struct fb_var_screeninfo *var = &fb->var;
+ unsigned int data;
+
+ /* enable low power mode & disable ACK */
+ data = 0x1f8;
+ /* burst with Sync pulses */
+ data |= 2 << 1;
+ writel_relaxed(data, info->reg_base + DSI_VID_MODE_CFG);
+ data = var->xres & 0x7ff;
+ writel_relaxed(data, info->reg_base + DSI_VID_PKT_CFG);
+
+ /* only enable BTA, Bus Turn-Around */
+ data = readl_relaxed(info->reg_base + DSI_PCKHDL_CFG);
+ data &= ~0x1f;
+ data |= 1 << 2;
+ writel_relaxed(data, info->reg_base + DSI_PCKHDL_CFG);
+}
+#endif
+
+void hi3620_mipi_dsi_set_video_packet_size(void __iomem *reg_base,
+ int null_pkt_size, int num_vid_pkt,
+ int vid_pkt_size)
+{
+ unsigned int data;
+ data = (null_pkt_size & 0x3ff) << 21; /* byte size */
+ /* number of video packets for Each Multiple Packets */
+ data |= (num_vid_pkt & 0x3ff) << 11;
+ /* pixels of each video packet */
+ data |= vid_pkt_size & 0x7ff;
+ writel_relaxed(data, reg_base + DSI_VID_PKT_CFG);
+}
+
+static void hi3620_mipi_setup_dpi(struct hi3620fb_info *info)
+{
+ unsigned int data;
+
+ /* set lane numbers */
+ /*
+ data = readl_relaxed(info->reg_base + DSI_PHY_IF_CFG) & ~0x3;
+ data |= (info->lane_cnt - 1) & 0x3;
+ writel_relaxed(data, info->reg_base + DSI_PHY_IF_CFG);
+ */
+ data = 0;
+ //data = readl_relaxed(info->reg_base + DSI_DPI_CFG);
+ /* set virtual channel ID as 0 */
+ data &= ~(3 << 0);
+ /* set color mode */
+ data &= ~(7 << 2);
+ data |= info->color_mode << 2;
+ /* always set color mode & shutdown high active */
+ writel_relaxed(data, info->reg_base + DSI_DPI_CFG);
+}
+
+static void hi3620_mipi_setup_phy(struct hi3620fb_info *info)
+{
+ struct clk *parent;
+ unsigned char hs2lp = 0, lp2hs = 0, hsfreq = 0;
+ unsigned int data;
+ int rate, rate_mhz, i;
+ struct phy_timing timing[] = {
+ {90, 24, 14, 0}, {100, 25, 14, 0x20},
+ {110, 25, 14, 0x40}, {125, 25, 14, 0x02},
+ {140, 25, 14, 0x22}, {150, 25, 14, 0x42},
+ {160, 25, 14, 0x04}, {180, 28, 16, 0x24},
+ {200, 32, 16, 0x44}, {210, 31, 16, 0x06},
+ {240, 35, 17, 0x26}, {250, 37, 18, 0x46},
+ {270, 37, 18, 0x08}, {300, 39, 19, 0x28},
+ {330, 44, 20, 0x08}, {360, 47, 21, 0x2a},
+ {400, 48, 21, 0x4a}, {450, 54, 23, 0x0c},
+ {500, 58, 25, 0x2c}, {550, 62, 26, 0x0e},
+ {600, 67, 28, 0x2e}, {650, 72, 30, 0x10},
+ {700, 76, 31, 0x30}, {750, 81, 32, 0x12},
+ {800, 86, 34, 0x32}, {850, 89, 35, 0x14},
+ {900, 95, 37, 0x34}, {950, 99, 38, 0x54},
+ {1000, 104, 40, 0x74}, };
+ parent = clk_get_parent(info->clk_dsi);
+ rate = clk_get_rate(parent);
+ rate_mhz = rate / 1000000;
+ for (i = 0; i < ARRAY_SIZE(timing); i++) {
+ if (rate_mhz <= timing[i].dsi2x) {
+ hs2lp = timing[i].hs2lp;
+ lp2hs = timing[i].lp2hs;
+ hsfreq = timing[i].hsfreq;
+ break;
+ }
+ }
+ /* setup PHY timing */
+ data = 4095; /* bta time */
+ data |= (lp2hs & 0xff) << 16;
+ data |= (hs2lp & 0xff) << 24;
+ writel_relaxed(data, info->reg_base + DSI_PHY_TMR_CFG);
+ /* set hsfreqrange */
+ hi3620_dsi_phy_write(info->reg_base, 0x44, hsfreq);
+ writel_relaxed(0x7, info->reg_base + DSI_PHY_RSTZ);
+}
+
+void hi3620_mipi_dsi_set_lane(void __iomem *reg_base, int id, int count)
+{
+ unsigned int data, cnt;
+
+ cnt = count - 1;
+ if (cnt < 0 || id > cnt)
+ return;
+ data = readl_relaxed(reg_base + DSI_PHY_IF_CFG) & ~0x3;
+ data |= cnt & 0x3;
+ writel_relaxed(data, reg_base + DSI_PHY_IF_CFG);
+ data = readl_relaxed(reg_base + DSI_DPI_CFG) & ~0x3;
+ data |= id & 0x3;
+ writel_relaxed(data, reg_base + DSI_DPI_CFG);
+}
+
+/*
+ * PHY_TST_CTRL0 & PHY_TST_CTRL1 registers are the interfaces of accessing
+ * PHY internal registers.
+ * PHY_TST_CTRL0 is used to produce clock, as I2C SCLK.
+ * PHY_TST_CTRL1 is used to store address or data, as I2C SDA.
+ */
+static void set_phy_testclk(void __iomem *reg_base, int level)
+{
+ unsigned int data;
+
+ if (level)
+ data = 0x2;
+ else
+ data = 0;
+ writel_relaxed(data, reg_base + DSI_PHY_TST_CTRL0);
+}
+
+/* write 8-bit data into 8-bit phy register */
+int hi3620_dsi_phy_write(void __iomem *reg_base, unsigned char addr,
+ unsigned char data)
+{
+ unsigned int value;
+
+ set_phy_testclk(reg_base, 0);
+ value = (unsigned int)addr | PHY_ADDR;
+ writel_relaxed(value, reg_base + DSI_PHY_TST_CTRL1);
+ set_phy_testclk(reg_base, 1);
+
+ set_phy_testclk(reg_base, 0);
+ value = (unsigned int)data;
+ writel_relaxed(value, reg_base + DSI_PHY_TST_CTRL1);
+ set_phy_testclk(reg_base, 1);
+ set_phy_testclk(reg_base, 0);
+ return 0;
+}
+EXPORT_SYMBOL(hi3620_dsi_phy_write);
+
+/* read 8-bit data from 8-bit phy register */
+unsigned char hi3620_dsi_phy_read(void __iomem *reg_base, unsigned char addr)
+{
+ unsigned int value;
+
+ set_phy_testclk(reg_base, 0);
+ value = (unsigned int)addr | PHY_ADDR;
+ writel_relaxed(value, reg_base + DSI_PHY_TST_CTRL1);
+ set_phy_testclk(reg_base, 1);
+ set_phy_testclk(reg_base, 0);
+ value = readl_relaxed(reg_base + DSI_PHY_TST_CTRL1);
+ return (unsigned char)(value >> 8);
+}
+
+static struct hi3620fb_info *hinfo = NULL;
+
+extern int sharp_display_on(void);
+
+int hi3620_mipi_enable(struct hi3620fb_info *info)
+{
+ if (!hinfo)
+ hinfo = info;
+ hi3620_mipi_enable_phy(info->reg_base);
+ hi3620_mipi_reset(info->reg_base);
+ //hi3620_mipi_switch_cmd_mode(info->reg_base); /* debug for keeping display on after boot */
+ //hi3620_mipi_unset_highspeed(info->reg_base); /* debug for keeping display on after boot */
+ hi3620_mipi_unreset(info->reg_base);
+
+ clk_prepare_enable(info->clk_dsi);
+ clk_prepare_enable(info->clk_lane);
+ clk_set_rate(info->clk_dsi, info->dsi_rate); /* huawei logo is shifted to right & color may be changed??? */
+
+ hi3620_mipi_setup_phy(info);
+
+ if (!strncmp(info->mipi_mode_name, "video", 5))
+ hi3620_mipi_switch_video_mode(info->reg_base);
+ else if (!strncmp(info->mipi_mode_name, "command", 7))
+ hi3620_mipi_switch_cmd_mode(info->reg_base);
+ else
+ return -EINVAL;
+ hi3620_mipi_set_highspeed(info->reg_base);
+ hi3620_mipi_setup_dpi(info); /* debug for keeping display on after boot */
+ return 0;
+}
+EXPORT_SYMBOL(hi3620_mipi_enable);
+
+int hi3620_mipi_disable(struct hi3620fb_info *info)
+{
+ hi3620_mipi_reset(info->reg_base);
+ hi3620_mipi_switch_cmd_mode(info->reg_base);
+ hi3620_mipi_unset_highspeed(info->reg_base);
+ hi3620_mipi_unreset(info->reg_base);
+ hi3620_mipi_disable_phy(info->reg_base);
+ return 0;
+}
+EXPORT_SYMBOL(hi3620_mipi_disable);
+
+int send_generic_packet(u8 *cmd, int len)
+{
+ unsigned int head = 0, data = 0;
+ int i;
+
+ if (len <= 0 || !hinfo || !cmd)
+ return -EINVAL;
+ switch (len) {
+ case 2:
+ head |= cmd[1] << 16;
+ /* fallthrough */
+ case 1:
+ head |= cmd[0] << 8;
+ head |= (len & 0x3) << 4;
+ head |= DTYPE_GEN_WRITE_HEAD;
+ break;
+ default:
+ head |= DTYPE_GEN_WRITE_PAYLOAD;
+ head |= (len & 0xffff) << 8;
+ for (i = 0; i < len; i += 4) {
+ data = 0;
+ data |= *(u32 *)(cmd + i);
+ writel_relaxed(data, hinfo->reg_base + DSI_GEN_PLD_DATA);
+ }
+ break;
+ }
+ /* write head to trigger a packet transfer */
+ writel_relaxed(data, hinfo->reg_base + DSI_GEN_HDR);
+ return len;
+}
+EXPORT_SYMBOL(send_generic_packet);
diff --git a/drivers/video/hisilicon/hi3620_fb.c b/drivers/video/hisilicon/hi3620_fb.c
new file mode 100644
index 000000000000..83cce2022743
--- /dev/null
+++ b/drivers/video/hisilicon/hi3620_fb.c
@@ -0,0 +1,650 @@
+/*
+ * Framebuffer driver of Hisilicon Hi3620 SoC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2013 Hisilicon Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <video/of_display_timing.h>
+#include <video/display_timing.h>
+#include "hi3620_fb.h"
+
+static unsigned int hi3620fb_pseudo_palette[16] = {
+ 0, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
+ ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
+};
+
+static int match_fmt_555(struct fb_var_screeninfo *var)
+{
+ if (var->blue.offset == 0 && var->green.offset <= 5 &&
+ var->red.offset <= 10 && var->transp.offset <= 15 &&
+ var->blue.offset <= 5 && var->green.length <= 5 &&
+ var->red.length <= 5)
+ return 1;
+ return 0;
+}
+
+static int match_fmt_565(struct fb_var_screeninfo *var)
+{
+ if (var->blue.offset == 0 && var->green.offset <= 5 &&
+ var->red.offset <= 11 && var->transp.offset == 0 &&
+ var->blue.length <= 5 && var->green.length <= 6 &&
+ var->red.length <= 5 && var->transp.length == 0)
+ return 1;
+ return 0;
+}
+
+static int match_fmt_888(struct fb_var_screeninfo *var)
+{
+ if (var->blue.offset == 0 && var->green.offset <= 8 &&
+ var->red.offset <= 16 && var->transp.offset <= 24 &&
+ var->blue.length <= 8 && var->green.length <= 8 &&
+ var->red.length <= 8)
+ return 1;
+ return 0;
+}
+
+static int find_best_pix_fmt(struct fb_var_screeninfo *var)
+{
+ if (var->bits_per_pixel == 16) {
+ /* RGB565/RGBA5551/RGBX5551 */
+ if (match_fmt_555(var)) {
+ if (var->transp.length == 1)
+ return IMG_PIXEL_FORMAT_ARGB1555;
+ else if (var->transp.length == 0)
+ return IMG_PIXEL_FORMAT_RGB555;
+ } else if (match_fmt_565(var))
+ return IMG_PIXEL_FORMAT_RGB565;
+ } else if (var->bits_per_pixel == 32) {
+ if (match_fmt_888(var)) {
+ if (var->transp.length == 8)
+ return IMG_PIXEL_FORMAT_ARGB8888;
+ else if (var->transp.length == 0)
+ return IMG_PIXEL_FORMAT_RGB888;
+ }
+ }
+ return -EINVAL;
+}
+
+static void set_pix_fmt(struct hi3620fb_info *info, int pix_fmt)
+{
+ struct fb_info *fb = info->fb;
+ struct fb_var_screeninfo *var = &fb->var;
+
+ switch (pix_fmt) {
+ case IMG_PIXEL_FORMAT_RGB565:
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ var->bits_per_pixel = 16;
+ break;
+ case IMG_PIXEL_FORMAT_ARGB1555:
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->transp.offset = 15;
+ var->transp.length = 1;
+ var->bits_per_pixel = 16;
+ break;
+ case IMG_PIXEL_FORMAT_RGB555:
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->transp.offset = 15;
+ var->transp.length = 0;
+ var->bits_per_pixel = 16;
+ break;
+ case IMG_PIXEL_FORMAT_ARGB8888:
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ var->bits_per_pixel = 32;
+ break;
+ case IMG_PIXEL_FORMAT_RGB888:
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 0;
+ var->bits_per_pixel = 32;
+ break;
+ default:
+ return;
+ }
+ info->pix_fmt = pix_fmt;
+}
+
+static int hi3620fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *fb)
+{
+ struct hi3620fb_info *info = fb->par;
+ int pix_fmt;
+
+ /*
+ * Determine which pixel format we're going to use.
+ */
+ pix_fmt = find_best_pix_fmt(var);
+ if (pix_fmt < 0)
+ return pix_fmt;
+ set_pix_fmt(info, pix_fmt);
+
+ /*
+ * Basic geometry sanity checks.
+ */
+ if (var->xoffset + var->xres > var->xres_virtual)
+ return -EINVAL;
+ if (var->yoffset + var->yres > var->yres_virtual)
+ return -EINVAL;
+ if (var->xres + var->right_margin +
+ var->hsync_len + var->left_margin > 2048)
+ return -EINVAL;
+ if (var->yres + var->lower_margin +
+ var->vsync_len + var->upper_margin > 2048)
+ return -EINVAL;
+
+ /*
+ * Check size of framebuffer.
+ */
+ if (var->xres_virtual * var->yres_virtual *
+ (var->bits_per_pixel >> 3) > fb->fix.smem_len)
+ return -EINVAL;
+ return 0;
+}
+
+/* It's used to make EDC configuration work. */
+static void update_edc(void __iomem *base)
+{
+ unsigned int data;
+
+ data = readl_relaxed(base + EDC_DISP_CTL);
+ writel_relaxed(data | EDC_CFG_OK, base + EDC_DISP_CTL);
+ data &= ~EDC_CFG_OK;
+ writel_relaxed(data, base + EDC_DISP_CTL);
+}
+
+static void set_panel_control(struct fb_info *fb)
+{
+ struct fb_videomode *fb_vm = fb->mode;
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+ u32 ldi, dpi;
+
+ ldi = readl_relaxed(base + LDI_PLR_CTRL) & ~LDI_POLARITY_MASK;
+ dpi = readl_relaxed(base + DSI_DPI_CFG) & ~DSI_DPI_POLARITY_MASK;
+ if (fb_vm->sync & FB_SYNC_HOR_HIGH_ACT) {
+ ldi &= ~LDI_HSYNC_POLARITY;
+ dpi &= ~DSI_DPI_HSYNC_POLARITY;
+ } else {
+ ldi |= LDI_HSYNC_POLARITY;
+ dpi |= DSI_DPI_HSYNC_POLARITY;
+ }
+ if (fb_vm->sync & FB_SYNC_VERT_HIGH_ACT) {
+ ldi &= ~LDI_VSYNC_POLARITY;
+ dpi &= ~DSI_DPI_VSYNC_POLARITY;
+ } else {
+ ldi |= LDI_VSYNC_POLARITY;
+ dpi |= DSI_DPI_VSYNC_POLARITY;
+ }
+ if (fb_vm->flag & FB_FLAG_DE_HIGH) {
+ ldi &= ~LDI_DATAEN_POLARITY;
+ dpi &= ~DSI_DPI_DATAEN_POLARITY;
+ }
+ if (fb_vm->flag & FB_FLAG_DE_LOW) {
+ ldi |= LDI_DATAEN_POLARITY;
+ dpi |= DSI_DPI_DATAEN_POLARITY;
+ }
+ if (fb_vm->flag & FB_FLAG_PIXDATA_POSEDGE)
+ ldi |= LDI_PIXELCLK_POLARITY;
+ if (fb_vm->flag & FB_FLAG_PIXDATA_NEGEDGE)
+ ldi &= ~LDI_PIXELCLK_POLARITY;
+ writel_relaxed(ldi, base + LDI_PLR_CTRL);
+ /* always set color mode & shutdown high active */
+ writel_relaxed(dpi, info->reg_base + DSI_DPI_CFG);
+}
+
+static void set_screen_dimensions(struct fb_info *fb)
+{
+ struct fb_var_screeninfo *var = &fb->var;
+ struct fb_videomode *fb_vm = fb->mode;
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+ unsigned long long int tmp;
+ u32 data, lane_rate, timing;
+
+ data = (var->left_margin & 0xfff) << 20;
+ data |= var->right_margin & 0xfff;
+ writel_relaxed(data, base + LDI_HRZ_CTRL0);
+ data = (var->hsync_len - 1) & 0xfff;
+ writel_relaxed(data, base + LDI_HRZ_CTRL1);
+ data = (var->upper_margin & 0xfff) << 20;
+ data |= var->lower_margin & 0xfff;
+ writel_relaxed(data, base + LDI_VRT_CTRL0);
+ data = (var->vsync_len - 1) & 0xfff;
+ writel_relaxed(data, base + LDI_VRT_CTRL1);
+
+ data = (var->xres - 1) & 0xfff;
+ data |= ((var->yres - 1) & 0xfff) << 20;
+ writel_relaxed(data, base + LDI_DSP_SIZE);
+
+ data = (fb->var.xres_virtual - 1) << 16 | (fb->var.yres_virtual - 1);
+ writel_relaxed(data, base + EDC_VIDEO_CHAN_SIZE);
+
+ /* setup line timing */
+ lane_rate = clk_get_rate(info->clk_lane);
+ data = fb_vm->hsync_len * lane_rate / fb_vm->pixclock;
+ timing = data & 0x1ff;
+ data = fb_vm->left_margin * lane_rate / fb_vm->pixclock;
+ timing |= (data & 0x1ff) << 9;
+ tmp = (unsigned long long int)(fb_vm->left_margin + fb_vm->xres +
+ fb_vm->right_margin + fb_vm->hsync_len);
+ tmp *= lane_rate;
+ do_div(tmp, fb_vm->pixclock);
+ data = (unsigned int)tmp;
+ timing |= data << 18;
+ writel_relaxed(timing, info->reg_base + DSI_TMR_LINE_CFG);
+
+ /* setup frame timing */
+ timing = fb_vm->vsync_len & 0xf;
+ timing |= (fb_vm->upper_margin & 0x3f) << 4;
+ timing |= (fb_vm->lower_margin & 0x3f) << 10;
+ timing |= (fb_vm->yres & 0x7ff) << 16;
+ writel_relaxed(timing, info->reg_base + DSI_VTIMING_CFG);
+}
+
+static void set_graphics_start(struct fb_info *fb, int xoffset, int yoffset)
+{
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+ u32 data;
+
+ data = yoffset & 0xfff;
+ data |= (xoffset & 0xfff) << 16;
+ writel_relaxed(data, base + EDC_VIDEO_CHAN_XY);
+ /* setup dma address */
+ writel_relaxed(fb->fix.smem_start, base + EDC_VIDEO_CHAN_ADDR);
+}
+
+static void set_dma_control(struct fb_info *fb)
+{
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+
+ /* setup dma stride */
+ writel_relaxed(fb->fix.line_length, base + EDC_VIDEO_CHAN_STRIDE);
+}
+
+static int hi3620fb_set_par(struct fb_info *fb)
+{
+ struct fb_var_screeninfo *var = &fb->var;
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+ unsigned int ctrl = 0;
+
+ fb->fix.ypanstep = var->yres;
+
+ ctrl = readl_relaxed(base + EDC_VIDEO_CHAN_CTRL);
+ if (var->blue.offset)
+ ctrl |= EDC_CHAN_CTRL_BGR; /* BGR format */
+ ctrl &= ~(7 << 16);
+ switch (info->pix_fmt) {
+ case IMG_PIXEL_FORMAT_ARGB1555:
+ case IMG_PIXEL_FORMAT_RGB555:
+ break;
+ case IMG_PIXEL_FORMAT_RGB565:
+ ctrl |= 1 << 16;
+ break;
+ case IMG_PIXEL_FORMAT_RGB888:
+ ctrl |= 2 << 16;
+ break;
+ case IMG_PIXEL_FORMAT_ARGB8888:
+ ctrl |= 3 << 16;
+ break;
+ }
+ ctrl |= EDC_VIDEO_CHAN_CTRL_EN;
+ /* color key & rotate is always disabled, linear format */
+// ctrl |= 1 << 24; /* enable channel */
+// ctrl &= ~0xfff;
+// ctrl |= 0xa;
+// ctrl |= var->yres - 1; /* debug. add for interrupt */
+ writel_relaxed(ctrl, base + EDC_VIDEO_CHAN_CTRL);
+
+ set_panel_control(fb);
+ set_screen_dimensions(fb);
+ set_dma_control(fb);
+ update_edc(base);
+ return 0;
+}
+
+static int hi3620fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fb)
+{
+ return 0;
+}
+
+static struct fb_ops hi3620fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = hi3620fb_check_var,
+ .fb_set_par = hi3620fb_set_par,
+ .fb_pan_display = hi3620fb_pan_display,
+};
+
+static int hi3620_parse_dt(struct device_node *np, struct hi3620fb_info *info)
+{
+ const char *name;
+ int ret;
+
+ ret = of_property_read_u32(np, "hisilicon,dsi-clock-frequency",
+ &info->dsi_rate);
+ if (ret)
+ return ret;
+ ret = of_property_read_string(np, "hisilicon,mipi-mode", &name);
+ if (ret < 0)
+ return ret;
+ info->mipi_mode_name = kstrdup(name, GFP_KERNEL);
+ ret = of_property_read_u32(np, "hisilicon,mipi-lanes", &info->lane_cnt);
+ if (ret < 0)
+ return ret;
+ ret = of_property_read_u32(np, "hisilicon,color-mode", &info->color_mode);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int hi3620_init_mode(struct device_node *np, struct fb_info *fb)
+{
+ struct fb_fix_screeninfo *fix = &fb->fix;
+ struct fb_var_screeninfo *var = &fb->var;
+ struct display_timings *disp;
+ struct fb_videomode *fb_vm;
+ struct hi3620fb_info *info = fb->par;
+ const char *pix_name;
+ int ret = 0, pix_fmt, i, length;
+
+ fb_vm = kzalloc(sizeof(*fb_vm), GFP_KERNEL);
+ if (!fb_vm)
+ return -ENOMEM;
+ fb->mode = fb_vm;
+ disp = of_get_display_timings(np);
+ if (!disp)
+ return -ENOENT;
+ /* How to handle multiple display timings ???,
+ * add_videomode is implemented by register_framebuffer() */
+ for (i = 0; i < disp->num_timings; i++) {
+ ret = of_get_fb_videomode(np, fb_vm, i);
+ if (ret)
+ goto out;
+ ret = of_property_read_string(np, "hisilicon,pixel-format",
+ &pix_name);
+ if (ret)
+ goto out;
+ if (!strncmp(pix_name, "RGBA8888", 8))
+ pix_fmt = IMG_PIXEL_FORMAT_ARGB8888;
+ else if (!strncmp(pix_name, "RGBX8888", 8))
+ pix_fmt = IMG_PIXEL_FORMAT_RGB888;
+ else if (!strncmp(pix_name, "RGBA5551", 8))
+ pix_fmt = IMG_PIXEL_FORMAT_ARGB1555;
+ else if (!strncmp(pix_name, "RGBX5551", 8))
+ pix_fmt = IMG_PIXEL_FORMAT_RGB555;
+ else if (!strncmp(pix_name, "RGB565", 6))
+ pix_fmt = IMG_PIXEL_FORMAT_RGB565;
+ else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ set_pix_fmt(info, pix_fmt);
+ fb_videomode_to_var(var, fb_vm);
+ var->xres_virtual = fb_vm->xres;
+ var->yres_virtual = fb_vm->yres;
+ var->grayscale = 0;
+ var->accel_flags = FB_ACCEL_NONE;
+ /* Now assume that video mode is only 1 in DTS. */
+ //fb_add_videomode(&vm, &fb->modelist);
+ }
+ of_display_timings_exist(np);
+
+ fix->type_aux = 0;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ fix->ywrapstep = 0;
+ fix->mmio_start = 0; /* No MMIO address */
+ fix->mmio_len = 0; /* No MMIO address */
+ fix->accel = FB_ACCEL_NONE; /* No hardware accelerator */
+
+ length = var->xres_virtual * var->bits_per_pixel / 8;
+ fb->fix.line_length = ALIGN(length, 64);
+ fb->fix.smem_len = ALIGN(fb->fix.line_length * fb->var.yres_virtual,
+ PAGE_SIZE);
+ hi3620_parse_dt(np, info);
+out:
+ return ret;
+}
+
+static irqreturn_t edc_irq_handler(int irq, void *data)
+{
+ struct hi3620fb_info *info = data;
+
+ pr_err("#%s, %d, ints:0x%x, inte:0x%x\n",
+ __func__, __LINE__, readl_relaxed(info->reg_base + EDC_INTS),
+ readl_relaxed(info->reg_base + EDC_INTE));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ldi_irq_handler(int irq, void *data)
+{
+ struct hi3620fb_info *info = data;
+ u32 value;
+
+ value = readl_relaxed(info->reg_base + LDI_ORG_INT);
+ writel_relaxed(value, info->reg_base + LDI_INT_CLR);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dsi_irq_handler(int irq, void *data)
+{
+ return IRQ_HANDLED;
+}
+
+static int hi3620_fb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct hi3620fb_info *info;
+ struct resource *res;
+ struct fb_info *fb;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ fb = framebuffer_alloc(sizeof(*info), dev);
+ if (!fb) {
+ dev_err(dev, "failed to allocate framebuffer\n");
+ return -ENOMEM;
+ }
+ fb->dev = &pdev->dev;
+ info = fb->par;
+ info->fb = fb;
+ info->dev = &pdev->dev;
+ info->irq_edc = platform_get_irq_byname(pdev, "edc");
+ if (info->irq_edc < 0) {
+ ret = -ENOENT;
+ goto err_fb;
+ }
+ info->irq_ldi = platform_get_irq_byname(pdev, "ldi");
+ if (info->irq_ldi < 0) {
+ ret = -ENOENT;
+ goto err_fb;
+ }
+ info->irq_dsi = platform_get_irq_byname(pdev, "dsi");
+ if (info->irq_dsi < 0) {
+ ret = -ENOENT;
+ goto err_fb;
+ }
+
+ info->reg_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!info->reg_base) {
+ ret = -EADDRNOTAVAIL;
+ goto err_fb;
+ }
+ info->vedc = devm_regulator_get(dev, "vedc");
+ if (IS_ERR_OR_NULL(info->vedc)) {
+ if (IS_ERR(info->vedc)) {
+ dev_err(dev, "failed to get vedc regulator\n");
+ info->vedc = NULL;
+ }
+ }
+ info->clk_ldi = of_clk_get_by_name(np, "ldi");
+ if (IS_ERR(info->clk_ldi)) {
+ dev_err(dev, "failed to get ldi clock\n");
+ ret = PTR_ERR(info->clk_ldi);
+ goto err_fb;
+ }
+ info->clk_edc = of_clk_get_by_name(np, "edc");
+ if (IS_ERR(info->clk_edc)) {
+ dev_err(dev, "failed to get edc clock\n");
+ ret = PTR_ERR(info->clk_edc);
+ goto err_fb;
+ }
+ info->clk_dsi = of_clk_get_by_name(np, "dsi");
+ if (IS_ERR(info->clk_dsi)) {
+ dev_err(dev, "failed to get dsi clock\n");
+ ret = PTR_ERR(info->clk_dsi);
+ goto err_clk;
+ }
+ info->clk_lane = of_clk_get_by_name(np, "lane");
+ if (IS_ERR(info->clk_lane)) {
+ dev_err(dev, "failed to get lane clock\n");
+ ret = PTR_ERR(info->clk_lane);
+ goto err_clk;
+ }
+ if (info->vedc)
+ regulator_enable(info->vedc);
+ clk_prepare_enable(info->clk_ldi);
+ //clk_prepare_enable(info->clk_edc); /* debug for keep display on after boot */
+
+ fb->fbops = &hi3620fb_ops;
+ fb->pseudo_palette = &hi3620fb_pseudo_palette;
+
+ ret = hi3620_init_mode(np, fb);
+ if (ret)
+ goto err_clk;
+
+#if 1
+ fb->screen_base = dma_alloc_coherent(fb->dev, fb->fix.smem_len,
+ &info->fb_start_dma,
+ GFP_KERNEL);
+ fb->screen_size = fb->fix.smem_len;
+ fb->fix.smem_start = info->fb_start_dma;
+#else
+ /* debug for remapping the display region in bootloader */
+ info->fb_start_dma = PAGE_ALIGN(0x35b00130 + 0x40000000);
+ fb->fix.smem_start = info->fb_start_dma;
+ fb->screen_size = fb->fix.smem_len;
+ fb->screen_base = __va(info->fb_start_dma);
+#endif
+ hi3620_mipi_enable(info);
+ set_graphics_start(fb, 0, 0);
+ hi3620fb_set_par(fb);
+
+
+ /* clear IRQ status & enable IRQ */
+ writel_relaxed(0, info->reg_base + EDC_INTS);
+ writel_relaxed(0x2c8, info->reg_base + EDC_INTE);
+ writel_relaxed(0x4, info->reg_base + LDI_INT_EN); /* disable front porch int for debugging */
+ writel_relaxed(0x3fff, info->reg_base + LDI_INT_CLR);
+
+ ret = devm_request_irq(dev, info->irq_edc, edc_irq_handler,
+ IRQF_DISABLED, "edc", info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request edc irq\n");
+ goto err_clk;
+ }
+ ret = devm_request_irq(dev, info->irq_ldi, ldi_irq_handler,
+ IRQF_DISABLED, "ldi", info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request ldi irq\n");
+ goto err_clk;
+ }
+ ret = devm_request_irq(dev, info->irq_dsi, dsi_irq_handler,
+ IRQF_DISABLED, "dsi", info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request dsi irq\n");
+ goto err_clk;
+ }
+ ret = register_framebuffer(fb);
+ if (ret < 0) {
+ dev_err(dev, "failed to register hi3620 framebuffer\n");
+ goto err_clk;
+ }
+ platform_set_drvdata(pdev, info);
+ /* clock rate of ldi */
+ return 0;
+err_clk:
+ clk_disable_unprepare(info->clk_edc);
+ clk_disable_unprepare(info->clk_ldi);
+err_fb:
+ framebuffer_release(fb);
+ return ret;
+}
+
+static int hi3620_fb_remove(struct platform_device *pdev)
+{
+ struct hi3620fb_info *info = platform_get_drvdata(pdev);
+ hi3620_mipi_disable(info);
+ return 0;
+}
+
+static const struct of_device_id hi3620_fb_of_match[] = {
+ { .compatible = "hisilicon,hi3620-fb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hi3620_fb_of_match);
+
+static struct platform_driver hi3620_fb_driver = {
+ .probe = hi3620_fb_probe,
+ .remove = hi3620_fb_remove,
+ .driver = {
+ .name = "hi3620-fb",
+ .owner = THIS_MODULE,
+ .of_match_table = hi3620_fb_of_match,
+ },
+};
+module_platform_driver(hi3620_fb_driver);
diff --git a/drivers/video/hisilicon/hi3620_fb.h b/drivers/video/hisilicon/hi3620_fb.h
new file mode 100644
index 000000000000..676ff8e2e8f7
--- /dev/null
+++ b/drivers/video/hisilicon/hi3620_fb.h
@@ -0,0 +1,125 @@
+#ifndef __HI3620FB_H
+#define __HI3620FB_H
+
+#define EDC_ID 0x000
+
+/* Channel 1 */
+#define EDC_GRAPH_CHAN_ADDR 0x004
+#define EDC_GRAPH_CHAN_STRIDE 0x00c
+#define EDC_GRAPH_CHAN_XY 0x010
+#define EDC_GRAPH_CHAN_SIZE 0x014
+#define EDC_GRAPH_CHAN_CTRL 0x018
+#define EDC_GRAPH_CHAN_CKEY_MIN 0x01c
+#define EDC_GRAPH_CHAN_CKEY_MAX 0x020
+
+/* Channel 2 */
+#define EDC_VIDEO_CHAN_ADDR 0x024
+#define EDC_VIDEO_CHAN_STRIDE 0x02c
+#define EDC_VIDEO_CHAN_XY 0x030
+#define EDC_VIDEO_CHAN_SIZE 0x034
+#define EDC_VIDEO_CHAN_CTRL 0x038
+#define EDC_VIDEO_CHAN_CKEY_MIN 0x03c
+#define EDC_VIDEO_CHAN_CKEY_MAX 0x040
+
+#define EDC_GRAPH_CHAN_CTRL_EN (1 << 24)
+#define EDC_VIDEO_CHAN_CTRL_EN (1 << 22)
+#define EDC_CHAN_CTRL_BGR (1 << 19)
+
+#define EDC_DISP_CTL 0x094
+#define EDC_DISP_DPD 0x098
+#define EDC_STS 0x09c
+#define EDC_INTS 0x0a0
+#define EDC_INTE 0x0a4
+
+#define LDI_HRZ_CTRL0 0x800
+#define LDI_HRZ_CTRL1 0x804
+#define LDI_VRT_CTRL0 0x808
+#define LDI_VRT_CTRL1 0x80c
+#define LDI_PLR_CTRL 0x810
+#define LDI_DSP_SIZE 0x814
+#define LDI_INT_EN 0x81c
+#define LDI_CTRL 0x820
+#define LDI_ORG_INT 0x824
+#define LDI_MSK_INT 0x828
+#define LDI_INT_CLR 0x82c
+#define LDI_WORK_MODE 0x830
+#define LDI_HDMI_DSI_GT 0x834
+
+#define DSI_CMD_MOD_CTRL 0x83c
+#define DSI_TE_CTRL 0x840
+#define DSI_TE_HS_NUM 0x844
+#define DSI_TE_HS_WD 0x848
+#define DSI_TE_VS_WD 0x84c
+
+#define DSI_PWR_UP 0x904
+#define DSI_CLKMGR_CFG 0x908
+#define DSI_DPI_CFG 0x90c
+#define DSI_PCKHDL_CFG 0x918
+#define DSI_VID_MODE_CFG 0x91c
+#define DSI_VID_PKT_CFG 0x920
+#define DSI_CMD_MODE_CFG 0x924
+#define DSI_TMR_LINE_CFG 0x928
+#define DSI_VTIMING_CFG 0x92c
+#define DSI_PHY_TMR_CFG 0x930
+#define DSI_GEN_HDR 0x934
+#define DSI_GEN_PLD_DATA 0x938
+#define DSI_ERROR_ST0 0x944
+#define DSI_ERROR_ST1 0x948
+#define DSI_ERROR_MSK0 0x94c
+#define DSI_ERROR_MSK1 0x950
+#define DSI_PHY_RSTZ 0x954
+#define DSI_PHY_IF_CFG 0x958
+#define DSI_PHY_IF_CTRL 0x95c
+#define DSI_PHY_TST_CTRL0 0x964
+#define DSI_PHY_TST_CTRL1 0x968
+
+/* bits field of EDC_DISP_CTL register */
+#define EDC_CFG_OK (1 << 1)
+
+/* bits field of LDI_PLT_CTRL register */
+#define LDI_POLARITY_MASK 0xf
+#define LDI_DATAEN_POLARITY (1 << 3)
+#define LDI_PIXELCLK_POLARITY (1 << 2)
+#define LDI_HSYNC_POLARITY (1 << 1)
+#define LDI_VSYNC_POLARITY (1 << 0)
+
+/* bits field of DSI_DPI_CFG register */
+#define DSI_DPI_POLARITY_MASK (0x1f << 5)
+#define DSI_DPI_COLORM_POLARITY (1 << 9)
+#define DSI_DPI_SHUTD_POLARITY (1 << 8)
+#define DSI_DPI_HSYNC_POLARITY (1 << 7)
+#define DSI_DPI_VSYNC_POLARITY (1 << 6)
+#define DSI_DPI_DATAEN_POLARITY (1 << 5)
+
+enum {
+ IMG_PIXEL_FORMAT_ARGB1555 = 0,
+ IMG_PIXEL_FORMAT_RGB555,
+ IMG_PIXEL_FORMAT_RGB565,
+ IMG_PIXEL_FORMAT_RGB888,
+ IMG_PIXEL_FORMAT_ARGB8888,
+};
+
+struct hi3620fb_info {
+ struct fb_info *fb;
+ struct device *dev;
+ void __iomem *reg_base;
+ struct clk *clk_ldi;
+ struct clk *clk_edc;
+ struct clk *clk_dsi;
+ struct clk *clk_lane;
+ int irq_edc;
+ int irq_ldi;
+ int irq_dsi;
+ struct regulator *vedc;
+ dma_addr_t fb_start_dma;
+ int pix_fmt;
+ int dsi_rate; /* dsi bit clock rate */
+ const char *mipi_mode_name;
+ int lane_cnt;
+ int color_mode;
+};
+
+extern int hi3620_mipi_enable(struct hi3620fb_info *info);
+extern int hi3620_mipi_disable(struct hi3620fb_info *info);
+extern int send_generic_packet(u8 *cmd, int len);
+#endif /* __HI3620FB_H */
diff --git a/include/linux/platform_data/hi3620-dsi.h b/include/linux/platform_data/hi3620-dsi.h
new file mode 100644
index 000000000000..447366f1e5c1
--- /dev/null
+++ b/include/linux/platform_data/hi3620-dsi.h
@@ -0,0 +1,32 @@
+/*
+ * Hisilicon Hi3620 MIPI DSI head file
+ *
+ * Copyright (c) 2013 Hisilicon Limited.
+ * Copyright (c) 2013 Linaro Limited.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * 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 __HI3620_DSI_H
+#define __HI3620_DSI_H
+
+int hi3620_dsi_phy_write(void __iomem *reg_base, unsigned char addr,
+ unsigned char data);
+unsigned char hi3620_dsi_phy_read(void __iomem *reg_base, unsigned char addr);
+
+#endif