aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuodong Xu <guodong.xu@linaro.org>2013-10-24 11:53:42 +0800
committerGuodong Xu <guodong.xu@linaro.org>2013-10-24 11:53:42 +0800
commitd1b7fb8d9cf8bad1ae921655457cb31c0b8292e2 (patch)
tree06530c3c3ea69499eb2bab1d5ed87ec9fbbad488
parent0ec66e599589c8600ee53c51df9794953d508651 (diff)
parentfcba55f3f98726c20d2424a055372c6b5e669b5e (diff)
Merge commit 'tracking-hilt-fb-1023' into integration-hilt-ll-2013.11integration-hilt-ll-2013.11
* commit 'tracking-hilt-fb-1023': clk: hisilicon: enable mipi clock of hi3620 ARM: config: remove ARCH_IMX & ARCH_SPEAR ARM: config: enable fb in hs_defconfig mfd: r63306: add r63306 LCD controller fb: hi3620: enable framebuffer driver fb: append flags of de and pixdata
-rw-r--r--arch/arm/configs/hs_defconfig13
-rw-r--r--drivers/clk/hisilicon/Kconfig4
-rw-r--r--drivers/clk/hisilicon/Makefile1
-rw-r--r--drivers/clk/hisilicon/clk-hi3620-dsi.c364
-rw-r--r--drivers/mfd/Kconfig6
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/r63306.c327
-rw-r--r--drivers/video/Kconfig1
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/fbmon.c10
-rw-r--r--drivers/video/hisilicon/Kconfig11
-rw-r--r--drivers/video/hisilicon/Makefile2
-rw-r--r--drivers/video/hisilicon/hi3620_dsi.c626
-rw-r--r--drivers/video/hisilicon/hi3620_fb.c926
-rw-r--r--drivers/video/hisilicon/hi3620_fb.h208
-rw-r--r--include/linux/mfd/r63306.h45
-rw-r--r--include/linux/platform_data/hi3620-dsi.h53
-rw-r--r--include/uapi/linux/fb.h5
18 files changed, 2593 insertions, 11 deletions
diff --git a/arch/arm/configs/hs_defconfig b/arch/arm/configs/hs_defconfig
index 9bde9c314a23..3eb9ae4e2ba7 100644
--- a/arch/arm/configs/hs_defconfig
+++ b/arch/arm/configs/hs_defconfig
@@ -9,11 +9,6 @@ CONFIG_ARCH_BCM=y
CONFIG_GPIO_PCA953X=y
CONFIG_ARCH_HIGHBANK=y
CONFIG_ARCH_KEYSTONE=y
-CONFIG_ARCH_MXC=y
-CONFIG_MACH_IMX51_DT=y
-CONFIG_SOC_IMX53=y
-CONFIG_SOC_IMX6Q=y
-CONFIG_SOC_IMX6SL=y
CONFIG_SOC_VF610=y
CONFIG_ARCH_OMAP3=y
CONFIG_ARCH_OMAP4=y
@@ -23,10 +18,6 @@ CONFIG_SOC_AM43XX=y
CONFIG_ARCH_ROCKCHIP=y
CONFIG_ARCH_HI3xxx=y
CONFIG_ARCH_SOCFPGA=y
-CONFIG_PLAT_SPEAR=y
-CONFIG_ARCH_SPEAR13XX=y
-CONFIG_MACH_SPEAR1310=y
-CONFIG_MACH_SPEAR1340=y
CONFIG_ARCH_STI=y
CONFIG_ARCH_SUNXI=y
CONFIG_ARCH_SIRF=y
@@ -101,6 +92,7 @@ CONFIG_PINCTRL_SINGLE=y
CONFIG_GPIO_GENERIC_PLATFORM=y
CONFIG_GPIO_TWL4030=y
CONFIG_MFD_HI6421_PMIC=y
+CONFIG_MFD_R63306=y
CONFIG_REGULATOR_GPIO=y
CONFIG_REGULATOR_AB8500=y
CONFIG_REGULATOR_TPS51632=y
@@ -108,12 +100,13 @@ CONFIG_REGULATOR_TPS62360=y
CONFIG_REGULATOR_TWL4030=y
CONFIG_REGULATOR_VEXPRESS=y
CONFIG_REGULATOR_HI6421=y
-CONFIG_DRM=y
CONFIG_TEGRA_HOST1X=y
CONFIG_DRM_TEGRA=y
CONFIG_FB_ARMCLCD=y
CONFIG_FB_WM8505=y
CONFIG_FB_SIMPLE=y
+CONFIG_FB=y
+CONFIG_FB_HI3620=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig
index 344ddb764c1f..1f8b3d3a2ed6 100644
--- a/drivers/clk/hisilicon/Kconfig
+++ b/drivers/clk/hisilicon/Kconfig
@@ -1,3 +1,7 @@
config HI3xxx_CLK_CORE
bool "Core clock driver of Hi3xxx Soc"
default y if COMMON_CLK
+
+config HI3620_CLK_MIPI_DSI
+ bool "MIPI DSI clock driver of Hi3620 SoC"
+ default y if COMMON_CLK && FB_HI3620
diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile
index 682b8dc12b04..e25405e712d9 100644
--- a/drivers/clk/hisilicon/Makefile
+++ b/drivers/clk/hisilicon/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_HI3xxx_CLK_CORE) += clk-hi3xxx.o clk-hi3716.o
+obj-$(CONFIG_HI3620_CLK_MIPI_DSI) += clk-hi3620-dsi.o
diff --git a/drivers/clk/hisilicon/clk-hi3620-dsi.c b/drivers/clk/hisilicon/clk-hi3620-dsi.c
new file mode 100644
index 000000000000..c89e8ff1aa81
--- /dev/null
+++ b/drivers/clk/hisilicon/clk-hi3620-dsi.c
@@ -0,0 +1,364 @@
+/*
+ * Hisilicon Hi3620 MIPI DSI clock driver
+ *
+ * Copyright (c) 2012-2013 Hisilicon Limited.
+ * Copyright (c) 2012-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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/clk-private.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/platform_data/hi3620-dsi.h>
+
+#define DSI_CLKMGR_CFG 0x908
+#define DSI_PHY_RSTZ 0x954
+
+enum {
+ HI3620_EDC,
+};
+
+struct clk_phy {
+ struct clk_hw hw;
+ void __iomem *reg_base;
+ unsigned int mult;
+ unsigned int div;
+};
+
+struct clk_esc {
+ struct clk_hw hw;
+ void __iomem *reg_base;
+ unsigned int div;
+};
+
+struct hs_clk {
+ void __iomem *edc;
+ spinlock_t lock;
+};
+
+static void __iomem __init *hi3620_init_clocks(struct device_node *np);
+
+static struct hs_clk hi3620_clk;
+
+struct phy_mult {
+ int mult;
+ int cp_current;
+ int lpf_ctrl;
+};
+
+static unsigned long clk_phy_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_phy *phy = container_of(hw, struct clk_phy, hw);
+ unsigned long long int rate;
+
+ if (!phy->div)
+ return 0;
+ rate = (unsigned long long int)parent_rate * phy->mult;
+ do_div(rate, phy->div);
+ return (unsigned long)rate;
+}
+
+static long clk_phy_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_phy *phy = container_of(hw, struct clk_phy, hw);
+ struct clk *clk_parent;
+ unsigned long long int mult, new_rate;
+
+ clk_parent = __clk_get_parent(hw->clk);
+ *prate = __clk_get_rate(clk_parent);
+ mult = (unsigned long long int)rate * phy->div;
+ do_div(mult, *prate);
+ new_rate = *prate * mult;
+ do_div(new_rate, phy->div);
+ return (unsigned long)new_rate;
+}
+
+static int clk_phy_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_phy *phy = container_of(hw, struct clk_phy, hw);
+ void __iomem *base = phy->reg_base;
+ unsigned long long int mult;
+ unsigned char cp_current, lpf_ctrl;
+ int i;
+ struct phy_mult m[] = { {32, 0x6, 0x10}, {64, 0x6, 0x10},
+ {128, 0xc, 0x8}, {256, 0x4, 0x4},
+ {512, 0x0, 0x1}, {768, 0x1, 0x1},
+ {1000, 0x2, 0x1}, };
+ mult = (unsigned long long int)rate * phy->div;
+ do_div(mult, parent_rate);
+ phy->mult = (unsigned int)mult;
+
+ for (i = 0; i < ARRAY_SIZE(m); i++) {
+ if (phy->mult <= m[i].mult) {
+ cp_current = m[i].cp_current;
+ lpf_ctrl = m[i].lpf_ctrl;
+ break;
+ }
+ }
+ if (i >= ARRAY_SIZE(m))
+ return -EINVAL;
+ lpf_ctrl |= 0xc0; /* bypass CP & LPF default values */
+ /* write CP current */
+ hi3620_dsi_phy_write(base, 0x11, cp_current);
+ /* write LPF control */
+ hi3620_dsi_phy_write(base, 0x12, lpf_ctrl);
+ /* configure N and M factors effectively */
+ hi3620_dsi_phy_write(base, 0x19, 0x33);
+ /* write N divider */
+ hi3620_dsi_phy_write(base, 0x17, phy->div - 1);
+ /* write M multiplier 1 */
+ hi3620_dsi_phy_write(base, 0x18, (phy->mult - 1) & 0x1f);
+ /* write M multiplier 2 */
+ hi3620_dsi_phy_write(base, 0x18, ((phy->mult - 1) >> 5) | 0x80);
+ /* set PLL unlocking filter */
+ hi3620_dsi_phy_write(base, 0x16, 0xff);
+ return 0;
+}
+
+struct clk_ops clk_phy_ops = {
+ .round_rate = clk_phy_round_rate,
+ .set_rate = clk_phy_set_rate,
+ .recalc_rate = clk_phy_recalc_rate,
+};
+
+static struct clk *clk_phy_register(struct device *dev, const char *name,
+ const char *parent_name, void __iomem *reg,
+ unsigned long flags)
+{
+ struct clk_phy *phy;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ pr_err("%s: could not allocate dsi phy clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_phy assignments */
+ phy->hw.init = &init;
+ phy->div = 13; /* since parent rate is always 26MHz */
+ phy->reg_base = reg;
+
+ init.name = name;
+ init.ops = &clk_phy_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(dev, &phy->hw);
+
+ if (IS_ERR(clk))
+ kfree(phy);
+
+ return clk;
+}
+
+void __init hi3620_phy_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *clk_name, **parent_names;
+ void __iomem *reg_base;
+
+ reg_base = hi3620_init_clocks(np);
+ if (!reg_base)
+ return;
+ if (of_property_read_string(np, "clock-output-names", &clk_name))
+ return;
+ /* gate only has the fixed parent */
+ parent_names = kzalloc(sizeof(char *), GFP_KERNEL);
+ if (!parent_names)
+ return;
+ parent_names[0] = of_clk_get_parent_name(np, 0);
+
+ clk = clk_phy_register(NULL, clk_name, parent_names[0], reg_base, 0);
+ if (IS_ERR(clk))
+ goto err;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return;
+err:
+ kfree(parent_names);
+}
+
+static int find_best_esc_divider(unsigned long parent_rate)
+{
+ unsigned int max_rate = 20000000; /* 20MHz */
+ unsigned int target_rate = 10000000; /* 10MHz */
+ unsigned int div, out_rate;
+
+ div = parent_rate / target_rate;
+ for (; div > 0; div--) {
+ out_rate = parent_rate / div;
+ if (out_rate < max_rate)
+ break;
+ }
+ if (div <= 0)
+ return 0;
+ return div;
+}
+
+static unsigned long clk_esc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_esc *esc = container_of(hw, struct clk_esc, hw);
+ unsigned int div;
+
+ if (!esc->div)
+ div = find_best_esc_divider(parent_rate);
+ else
+ div = esc->div;
+ if (!div)
+ return 0;
+ return parent_rate / div;
+}
+
+static long clk_esc_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk *clk_parent;
+ unsigned int div;
+
+ clk_parent = __clk_get_parent(hw->clk);
+ *prate = __clk_get_rate(clk_parent);
+
+ div = find_best_esc_divider(*prate);
+ return *prate / div;
+}
+
+static int clk_esc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_esc *esc = container_of(hw, struct clk_esc, hw);
+ unsigned int data;
+
+ esc->div = parent_rate / rate;
+ data = readl_relaxed(esc->reg_base + DSI_CLKMGR_CFG);
+ data &= ~0xff;
+ writel_relaxed(data | esc->div, esc->reg_base + DSI_CLKMGR_CFG);
+ return 0;
+}
+
+struct clk_ops clk_esc_ops = {
+ .round_rate = clk_esc_round_rate,
+ .set_rate = clk_esc_set_rate,
+ .recalc_rate = clk_esc_recalc_rate,
+};
+
+static struct clk *clk_esc_register(struct device *dev, const char *name,
+ const char *parent_name, void __iomem *reg,
+ unsigned long flags)
+{
+ struct clk_esc *esc;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ esc = kzalloc(sizeof(*esc), GFP_KERNEL);
+ if (!esc) {
+ pr_err("%s: could not allocate dsi esc clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_esc assignments */
+ esc->hw.init = &init;
+ esc->reg_base = reg;
+
+ init.name = name;
+ init.ops = &clk_esc_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(dev, &esc->hw);
+
+ if (IS_ERR(clk))
+ kfree(esc);
+
+ return clk;
+}
+
+void __init hi3620_esc_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *clk_name, **parent_names;
+ void __iomem *reg_base;
+
+ reg_base = hi3620_init_clocks(np);
+ if (!reg_base)
+ return;
+ if (of_property_read_string(np, "clock-output-names", &clk_name))
+ return;
+ /* gate only has the fixed parent */
+ parent_names = kzalloc(sizeof(char *), GFP_KERNEL);
+ if (!parent_names)
+ return;
+ parent_names[0] = of_clk_get_parent_name(np, 0);
+
+ clk = clk_esc_register(NULL, clk_name, parent_names[0], reg_base, 0);
+ if (IS_ERR(clk))
+ goto err;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return;
+err:
+ kfree(parent_names);
+}
+
+CLK_OF_DECLARE(hi3620_dsi_pll, "hisilicon,hi3620-phy", hi3620_phy_setup)
+CLK_OF_DECLARE(hi3620_dsi_esc, "hisilicon,hi3620-phy-esc", hi3620_esc_setup)
+
+static const struct of_device_id hi3620_of_match[] = {
+ { .compatible = "hisilicon,hi3620-fb", .data = (void *)HI3620_EDC, },
+};
+
+static void __iomem __init *hi3620_init_clocks(struct device_node *np)
+{
+ struct device_node *parent;
+ const struct of_device_id *match;
+ void __iomem *ret = NULL;
+
+ parent = of_get_parent(np);
+ if (!parent)
+ goto out;
+ match = of_match_node(hi3620_of_match, parent);
+ if (!match)
+ goto out;
+ switch ((unsigned int)match->data) {
+ case HI3620_EDC:
+ if (!hi3620_clk.edc) {
+ ret = of_iomap(parent, 0);
+ WARN_ON(!ret);
+ hi3620_clk.edc = ret;
+ } else {
+ ret = hi3620_clk.edc;
+ }
+ break;
+ }
+out:
+ return ret;
+}
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index efa878f67fb7..6c9892cbb312 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -476,6 +476,12 @@ config MFD_PM8XXX_IRQ
This is required to use certain other PM 8xxx features, such as GPIO
and MPP.
+config MFD_R63306
+ tristate "Support R63306 Graphics Liquid Crystal Controller"
+ select MFD_CORE
+ help
+ This supports for Renesas R63306 Graphics Liquid Crystal Controller.
+
config MFD_RDC321X
tristate "RDC R-321x southbridge"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index ec550d660ce0..c486ffe1a2a6 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -149,6 +149,7 @@ obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
+obj-$(CONFIG_MFD_R63306) += r63306.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
diff --git a/drivers/mfd/r63306.c b/drivers/mfd/r63306.c
new file mode 100644
index 000000000000..2bd0f4ad97bc
--- /dev/null
+++ b/drivers/mfd/r63306.c
@@ -0,0 +1,327 @@
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/r63306.h>
+
+#define RENESAS_ID_VENDOR 0x0122
+
+#define DSI_CMD_LEN 32
+
+enum DATA_TYPE {
+ GEN_SHORT_WR_PARAM0 = 0x03,
+ GEN_SHORT_WR_PARAM1 = 0x13,
+ GEN_SHORT_WR_PARAM2 = 0x23,
+ GEN_RD_PARAM0 = 0x04,
+ GEN_RD_PARAM1 = 0x14,
+ GEN_RD_PARAM2 = 0x24,
+ DCS_SHORT_WR_PARAM0 = 0x05,
+ DCS_SHORT_WR_PARAM1 = 0x15,
+ DCS_RD_PARAM0 = 0x06,
+ SET_MAX_PKT = 0x37,
+ NULL_PKT = 0x09,
+ BLANKING_PKT = 0x19,
+ GEN_LONG_WR = 0x29,
+ DCS_LONG_WR = 0x39,
+ PACKED_PIXEL_16B = 0x0e,
+ PACKED_PIXEL_18B = 0x1e,
+ LOOSELY_PACKED_PIXEL_18B = 0x2e,
+ PACKED_PIXEL_24B = 0x3e,
+};
+
+/* 1st byte is DT type, 2nd byte is command, others are parameters. */
+static u8 soft_reset[] = {DCS_SHORT_WR_PARAM0, 0x01};
+static u8 get_power_mode[] = {DCS_RD_PARAM0, 0x0a};
+static u8 get_address_mode[] = {DCS_RD_PARAM0, 0x0b};
+static u8 get_display_mode[] = {DCS_RD_PARAM0, 0x0d};
+static u8 get_signal_mode[] = {DCS_RD_PARAM0, 0x0e};
+static u8 enter_sleep_mode[] = {DCS_SHORT_WR_PARAM0, 0x10};
+static u8 exit_sleep_mode[] = {DCS_SHORT_WR_PARAM0, 0x11};
+static u8 exit_invert_mode[] = {DCS_SHORT_WR_PARAM0, 0x20};
+static u8 enter_invert_mode[] = {DCS_SHORT_WR_PARAM0, 0x21};
+static u8 set_display_off[] = {DCS_SHORT_WR_PARAM0, 0x28};
+static u8 set_display_on[] = {DCS_SHORT_WR_PARAM0, 0x29};
+static u8 set_address_mode[] = {DCS_SHORT_WR_PARAM1, 0x36, 0x0};
+static u8 read_dsi_ctrl[] = {GEN_RD_PARAM1, 0xb6};
+static u8 write_dsi_ctrl[] = {GEN_LONG_WR, 0xb6};
+static u8 read_pfm_pwm_ctrl[] = {GEN_RD_PARAM1, 0xb9};
+static u8 write_pfm_pwm_ctrl[] = {GEN_LONG_WR, 0xb9};
+static u8 read_backlight[] = {GEN_RD_PARAM1, 0xba};
+static u8 read_cabc_ctrl[] = {GEN_RD_PARAM1, 0xbb};
+static u8 write_cabc_ctrl[] = {GEN_LONG_WR, 0xbb};
+static u8 read_cabc_param[] = {GEN_RD_PARAM1, 0xbe};
+static u8 write_cabc_param[] = {GEN_LONG_WR, 0xbe};
+static u8 read_device_code[] = {GEN_RD_PARAM1, 0xbf};
+
+static struct mfd_cell pwm_devs[] = {
+ {
+ .name = "r63306-pwm",
+ .id = -1,
+ .of_compatible = "renesas,r63306-pwm",
+ },
+};
+
+extern int dsi_set_packet(u8 *cmd, int nr_payload);
+
+int dsi_reset(void)
+{
+ u8 cmd[DSI_CMD_LEN];
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, soft_reset, sizeof(soft_reset));
+ return dsi_set_packet(cmd, 1);
+}
+EXPORT_SYMBOL(dsi_reset);
+
+int dsi_enter_sleep(void)
+{
+ u8 cmd[DSI_CMD_LEN];
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, enter_sleep_mode, sizeof(enter_sleep_mode));
+ return dsi_set_packet(cmd, 1);
+}
+EXPORT_SYMBOL(dsi_enter_sleep);
+
+int dsi_exit_sleep(void)
+{
+ u8 cmd[DSI_CMD_LEN];
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, exit_sleep_mode, sizeof(exit_sleep_mode));
+ return dsi_set_packet(cmd, 1);
+}
+EXPORT_SYMBOL(dsi_exit_sleep);
+
+int dsi_get_id(struct r63306_device_id *id)
+{
+ u8 cmd[DSI_CMD_LEN];
+ int ret;
+
+pr_err("#%s, %d\n", __func__, __LINE__);
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, read_device_code, sizeof(read_device_code));
+ ret = dsi_set_packet(cmd, 5);
+ if (ret)
+ return ret;
+ id->vendor = cmd[2] << 8 | cmd[3];
+ id->product = cmd[4] << 8 | cmd[5];
+ id->revision = cmd[6];
+ return 0;
+}
+EXPORT_SYMBOL(dsi_get_id);
+
+int dsi_get_backlight(int *level)
+{
+ u8 cmd[DSI_CMD_LEN];
+ int ret;
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, read_backlight, sizeof(read_backlight));
+ ret = dsi_set_packet(cmd, 2);
+ if (ret)
+ return ret;
+ pr_err("#%s, %d, backlight:0x%x 0x%x\n", __func__, __LINE__, cmd[2], cmd[3]);
+ return 0;
+}
+
+int dsi_get_cabc(struct r63306_cabc *cabc)
+{
+ u8 cmd[DSI_CMD_LEN];
+ int ret;
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, read_cabc_ctrl, sizeof(read_cabc_ctrl));
+ ret = dsi_set_packet(cmd, 1);
+ if (ret)
+ return ret;
+ cabc->cabc_on = (cmd[2] & (1 << 0)) ? 1 : 0;
+ cabc->pwm_on = (cmd[2] & (1 << 1)) ? 1 : 0;
+ cabc->pfm_on = (cmd[2] & (1 << 2)) ? 1 : 0;
+ cabc->ledpwm_pin = (cmd[2] & (1 << 3)) ? 1 : 0;
+ cabc->ledpwm_pol = (cmd[2] & (1 << 4)) ? 1 : 0;
+ pr_err("#%s, %d, cabc:%d, pwm_on:%d, pfm_on:%d, ledpwm_pin:%d, ledpwm_pol:%d\n",
+ __func__, __LINE__, cabc->cabc_on, cabc->pwm_on, cabc->pfm_on, cabc->ledpwm_pin, cabc->ledpwm_pol);
+ return 0;
+}
+EXPORT_SYMBOL(dsi_get_cabc);
+
+int dsi_set_cabc(struct r63306_cabc *cabc)
+{
+ u8 cmd[DSI_CMD_LEN];
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, write_cabc_ctrl, sizeof(write_cabc_ctrl));
+ cmd[2] = (cabc->cabc_on << 0) | (cabc->pwm_on << 1) |
+ (cabc->pfm_on << 2) | (cabc->ledpwm_pin << 3) |
+ (cabc->ledpwm_pol << 4);
+ return dsi_set_packet(cmd, 1 + 1);
+}
+EXPORT_SYMBOL(dsi_set_cabc);
+
+int dsi_get_cabc_param(struct r63306_cabc_param *cabc)
+{
+ u8 cmd[DSI_CMD_LEN];
+ int ret;
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, read_cabc_param, sizeof(read_cabc_param));
+ ret = dsi_set_packet(cmd, 8);
+ if (ret < 0)
+ return ret;
+#if 0
+ cabc->backlight = ((cmd[2] & 0xf) << 4) | (cmd[3] & 0xf);
+#else
+ cabc->backlight = (cmd[2] << 8) | (cmd[3] & 0xf);
+#endif
+ pr_err("#%s, %d, [2]:0x%x, [3]:0x%x, [4]:0x%x\n",
+ __func__, __LINE__, cmd[2], cmd[3], cmd[4]);
+ return 0;
+}
+EXPORT_SYMBOL(dsi_get_cabc_param);
+
+int dsi_set_cabc_param(struct r63306_cabc_param *cabc)
+{
+ u8 cmd[DSI_CMD_LEN];
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, write_cabc_param, sizeof(write_cabc_param));
+/*
+#if 0
+ cmd[2] = (cabc->backlight >> 4) & 0xf;
+ cmd[3] = cabc->backlight & 0xf;
+ cmd[4] = 0x02;
+ cmd[5] = 0x02;
+ cmd[6] = 0x04;
+ cmd[7] = 0x04;
+ cmd[8] = 0x00;
+ cmd[9] = 0x5d;
+#else
+ cmd[2] = (cabc->backlight & 0xff0) >> 4;
+ cmd[3] = cabc->backlight & 0xf;
+#endif
+*/
+ cmd[2] = cabc->backlight;
+ cmd[3] = cabc->backlight & 0xf;
+ return dsi_set_packet(cmd, 2 + 1);
+}
+
+int dsi_get_pwm(struct r63306_pwm *pwm)
+{
+ u8 cmd[DSI_CMD_LEN];
+ int ret;
+ int i;
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, read_pfm_pwm_ctrl, sizeof(read_pfm_pwm_ctrl));
+ ret = dsi_set_packet(cmd, 12);
+ if (ret)
+ return ret;
+ pwm->divider = cmd[2];
+ pwm->duty_cycle = ((cmd[3] << 8) | cmd[4]) & 0x1ff;
+ pwm->pwm_wm = (cmd[3] & 0x10) ? 1 : 0;
+#if 1
+ pr_err("#%s, %d, divider:%d, duty_cycle:%d, pwm_wm:%d\n",
+ __func__, __LINE__, pwm->divider, pwm->duty_cycle, pwm->pwm_wm);
+ for (i = 0; i < 12; i++) {
+ pr_err("c[%d]:0x%x ", i, cmd[i]);
+ }
+ pr_err("\n");
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(dsi_get_pwm);
+
+int dsi_set_pwm(struct r63306_pwm *pwm)
+{
+#if 1
+ u8 cmd[DSI_CMD_LEN];
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, write_pfm_pwm_ctrl, sizeof(write_pfm_pwm_ctrl));
+ cmd[2] = pwm->divider;
+ cmd[3] = ((pwm->duty_cycle & 0x100) >> 8) | (pwm->pwm_wm << 4);
+ cmd[4] = pwm->duty_cycle & 0xff;
+ //cmd[4] = 0x0e;
+ return dsi_set_packet(cmd, 12 + 1);
+#else
+ u8 cmd[DSI_CMD_LEN];
+ int ret;
+
+ memset(cmd, 0, DSI_CMD_LEN);
+ memcpy(cmd, read_pfm_pwm_ctrl, sizeof(read_pfm_pwm_ctrl));
+ ret = dsi_set_packet(cmd, 12);
+ if (ret)
+ return ret;
+ cmd[0] = write_pfm_pwm_ctrl[0];
+ cmd[2] = pwm->divider;
+ cmd[3] = ((pwm->duty_cycle & 0x100) >> 8) | (pwm->pwm_wm << 4);
+ cmd[3] |= (1 << 4);
+ cmd[4] = pwm->duty_cycle & 0xff;
+ //cmd[4] = 0x0e;
+ cmd[5] = 0x73;
+ return dsi_set_packet(cmd, 12 + 1);
+#endif
+}
+EXPORT_SYMBOL(dsi_set_pwm);
+
+static int r63306_probe(struct platform_device *pdev)
+{
+ struct r63306_chip *chip;
+ struct r63306_device_id id;
+ struct r63306_cabc cabc;
+ int ret;
+
+ if (dsi_get_id(&id) < 0 || id.vendor != RENESAS_ID_VENDOR) {
+ dev_err(&pdev->dev, "Can't detect Renesas chip\n");
+ return -EPROBE_DEFER;
+ }
+
+ /* set pwm on */
+ ret = dsi_get_cabc(&cabc);
+ if (ret < 0)
+ return -EPROBE_DEFER;
+ //cabc.pwm_on = true;
+ ret = dsi_set_cabc(&cabc);
+ if (ret < 0)
+ return -EPROBE_DEFER;
+ chip = kzalloc(sizeof(struct r63306_chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ chip->dev = &pdev->dev;
+
+ ret = mfd_add_devices(chip->dev, 0, pwm_devs, ARRAY_SIZE(pwm_devs),
+ NULL, 0, NULL);
+ if (ret)
+ goto err;
+ return 0;
+err:
+ kfree(chip);
+ return ret;
+}
+
+static int r63306_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id r63306_of_match[] = {
+ { .compatible = "renesas,r63306", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, r63306_of_match);
+
+static struct platform_driver r63306_driver = {
+ .probe = r63306_probe,
+ .remove = r63306_remove,
+ .driver = {
+ .name = "r63306",
+ .owner = THIS_MODULE,
+ .of_match_table = r63306_of_match,
+ },
+};
+module_platform_driver(r63306_driver);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 84b685f7ab6e..7ff869d1d3b1 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2469,6 +2469,7 @@ config FB_SIMPLE
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
source "drivers/video/exynos/Kconfig"
+source "drivers/video/hisilicon/Kconfig"
source "drivers/video/mmp/Kconfig"
source "drivers/video/backlight/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e8bae8dd4804..dfc52137710f 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_FB_RIVA) += riva/
obj-$(CONFIG_FB_NVIDIA) += nvidia/
obj-$(CONFIG_FB_ATY) += aty/ macmodes.o
obj-$(CONFIG_FB_ATY128) += aty/ macmodes.o
+obj-$(CONFIG_FB_HI3620) += hisilicon/
obj-$(CONFIG_FB_RADEON) += aty/
obj-$(CONFIG_FB_SIS) += sis/
obj-$(CONFIG_FB_VIA) += via/
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index 6103fa6fb54f..f5c5f6644d17 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -1398,6 +1398,7 @@ int fb_videomode_from_videomode(const struct videomode *vm,
fbmode->sync = 0;
fbmode->vmode = 0;
+ fbmode->flag = 0;
if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH)
fbmode->sync |= FB_SYNC_HOR_HIGH_ACT;
if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH)
@@ -1406,7 +1407,14 @@ int fb_videomode_from_videomode(const struct videomode *vm,
fbmode->vmode |= FB_VMODE_INTERLACED;
if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN)
fbmode->vmode |= FB_VMODE_DOUBLE;
- fbmode->flag = 0;
+ if (vm->flags & DISPLAY_FLAGS_DE_HIGH)
+ fbmode->flag |= FB_FLAG_DE_HIGH;
+ if (vm->flags & DISPLAY_FLAGS_DE_LOW)
+ fbmode->flag |= FB_FLAG_DE_LOW;
+ if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
+ fbmode->flag |= FB_FLAG_PIXDATA_POSEDGE;
+ if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+ fbmode->flag |= FB_FLAG_PIXDATA_NEGEDGE;
htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
vm->hsync_len;
diff --git a/drivers/video/hisilicon/Kconfig b/drivers/video/hisilicon/Kconfig
new file mode 100644
index 000000000000..bb825982df9a
--- /dev/null
+++ b/drivers/video/hisilicon/Kconfig
@@ -0,0 +1,11 @@
+config FB_HI3620
+ tristate "Hisilicon Hi3620 Frame Buffer support"
+ depends on FB && ARCH_HI3xxx
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select FB_MODE_HELPERS
+ select VIDEOMODE_HELPERS
+ 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..e7140d2accb2
--- /dev/null
+++ b/drivers/video/hisilicon/hi3620_dsi.c
@@ -0,0 +1,626 @@
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.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 struct device *dsi_dev = NULL;
+
+static void reset(void __iomem *reg_base)
+{
+ writel_relaxed(0, reg_base + DSI_PWR_UP);
+}
+
+static void unreset(void __iomem *reg_base)
+{
+ writel_relaxed(1, reg_base + DSI_PWR_UP);
+}
+
+static void 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 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);
+}
+
+/* bit definition of register DSI_VID_MODE_CFG */
+#define VIDEO_MODE_EN (1 << 0)
+
+/* bit definition of register DSI_CMD_MODE_CFG */
+#define CMD_MODE_EN (1 << 0)
+#define CMD_MODE_LP (0x1ffe)
+
+static int is_video_mode(void __iomem *base)
+{
+ unsigned int data;
+
+ data = readl_relaxed(base + DSI_VID_MODE_CFG);
+ if (data & VIDEO_MODE_EN)
+ return 1;
+ return 0;
+}
+
+static int is_cmd_mode(void __iomem *base)
+{
+ unsigned int data;
+
+ data = readl_relaxed(base + DSI_CMD_MODE_CFG);
+ if (data & CMD_MODE_EN)
+ return 1;
+ return 0;
+}
+
+/* Switch to CMD mode with LP transmission */
+static void set_cmd_mode(void __iomem *base)
+{
+ unsigned int data;
+
+ /* disable VIDEO mode */
+ data = readl_relaxed(base + DSI_VID_MODE_CFG);
+ data &= ~VIDEO_MODE_EN;
+ writel_relaxed(data, base + DSI_VID_MODE_CFG);
+ /* enable COMMAND mode with LP transmission */
+ data = readl_relaxed(base + DSI_CMD_MODE_CFG);
+ data |= CMD_MODE_LP | CMD_MODE_EN;
+ writel_relaxed(data, base + DSI_CMD_MODE_CFG);
+}
+
+static void init_video_mode(void __iomem *base, struct hi3620fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->fb->var;
+ unsigned int data;
+
+ data = readl_relaxed(base + DSI_VID_MODE_CFG);
+ data |= 0x1f8; /* enable Low Power control */
+ data &= ~(3 << 1); /* mask video transfer mode */
+ data |= (2 << 1); /* Burst with Sync Pulses */
+ data &= ~0x800; /* Disable frame end ACK */
+ writel_relaxed(data, base + DSI_VID_MODE_CFG);
+ data = readl_relaxed(base + DSI_VID_PKT_CFG);
+ data &= ~0x7ff; /* mask single video packet size */
+ data |= var->xres;
+ writel_relaxed(data, base + DSI_VID_PKT_CFG);
+ data = readl_relaxed(base + DSI_PCKHDL_CFG);
+ data |= (1 << 2); /* enable Bus Turn-Around */
+ writel_relaxed(data, base + DSI_PCKHDL_CFG);
+}
+
+/* Switch to VIDEO mode */
+static void set_video_mode(void __iomem *base)
+{
+ unsigned int data;
+
+ /* disable CMD mode */
+ data = readl_relaxed(base + DSI_CMD_MODE_CFG);
+ data |= CMD_MODE_LP;
+ data &= ~CMD_MODE_EN;
+ writel_relaxed(data, base + DSI_CMD_MODE_CFG);
+ /* enable VIDEO mode */
+ data = readl_relaxed(base + DSI_VID_MODE_CFG);
+ data |= VIDEO_MODE_EN;
+ writel_relaxed(data, base + DSI_VID_MODE_CFG);
+}
+
+static void set_dpi_timing(struct hi3620fb_info *info)
+{
+ void __iomem *base = info->reg_base;
+ struct fb_videomode *fb_vm = info->fb->mode;
+ unsigned int dsi_rate, lane_rate, pixclock, data, timing;
+ unsigned long long int tmp;
+
+ dsi_rate = clk_get_rate(info->clk_dsi); /* dsi1x, not dsi2x */
+ lane_rate = dsi_rate / 4; /* dsi2x / 8 */
+
+ pixclock = PICOS2KHZ(fb_vm->pixclock) * 1000;
+ tmp = (unsigned long long int)(fb_vm->hsync_len) * lane_rate;
+ do_div(tmp, pixclock);
+ data = (unsigned int)tmp;
+ timing = data & 0x1ff;
+ tmp = (unsigned long long int)(fb_vm->left_margin) * lane_rate;
+ do_div(tmp, pixclock);
+ data = (unsigned int)tmp;
+ 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, 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);
+}
+
+#define PHY_TX_OUT_LP_DATA (1 << 4)
+#define PHY_TX_IN_LP_DATA (1 << 3)
+#define PHY_TX_OUT_LP_CLK (1 << 2)
+#define PHY_TX_IN_LP_CLK (1 << 1)
+#define PHY_TX_REQ_HS_CLK (1 << 0)
+#define PHY_TX_MASK 0x1f
+
+static int is_phy_hs(void __iomem *base)
+{
+ unsigned int data, expected;
+
+ data = readl_relaxed(base + DSI_PHY_IF_CTRL);
+ expected = PHY_TX_OUT_LP_DATA | PHY_TX_OUT_LP_CLK | PHY_TX_REQ_HS_CLK;
+ if ((data & PHY_TX_MASK) == expected)
+ return 1;
+ return 0;
+}
+
+static int is_phy_lp(void __iomem *base)
+{
+ unsigned int data, expected;
+
+ data = readl_relaxed(base + DSI_PHY_IF_CTRL);
+ expected = PHY_TX_IN_LP_DATA | PHY_TX_IN_LP_CLK;
+ if ((data & PHY_TX_MASK) == expected)
+ return 1;
+ return 0;
+}
+
+static int set_phy_hs(void __iomem *base)
+{
+ unsigned int data;
+
+ data = readl_relaxed(base + DSI_PHY_IF_CTRL);
+ data &= ~PHY_TX_MASK;
+ data |= PHY_TX_OUT_LP_DATA | PHY_TX_OUT_LP_CLK;
+ writel_relaxed(data, base + DSI_PHY_IF_CTRL);
+ data = readl_relaxed(base + DSI_PHY_IF_CTRL);
+ return 0;
+}
+
+static int set_phy_lp(void __iomem *base)
+{
+ unsigned int data;
+
+ data = readl_relaxed(base + DSI_PHY_IF_CTRL);
+ data &= ~PHY_TX_MASK;
+ data |= PHY_TX_OUT_LP_DATA | PHY_TX_OUT_LP_CLK;
+ writel_relaxed(data, base + DSI_PHY_IF_CTRL);
+ data = readl_relaxed(base + DSI_PHY_IF_CTRL);
+ return 0;
+}
+
+static void enable_phy(void __iomem *base)
+{
+ unsigned int status, mask;
+
+ writel_relaxed(0x7, base + DSI_PHY_RSTZ);
+ mask = DSI_PHY_STOP_STATE0_LANE | DSI_PHY_LOCK;
+ do {
+ cpu_relax();
+ status = readl_relaxed(base + DSI_PHY_STATUS);
+ } while ((status & mask) != mask);
+}
+
+static void disable_phy(void __iomem *reg_base)
+{
+ writel_relaxed(0, reg_base + DSI_PHY_RSTZ);
+}
+
+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);
+}
+
+#define MAX_TX_ESC_CLK (10 * 1000 * 1000)
+
+static void setup_phy(struct hi3620fb_info *info)
+{
+ struct clk *parent;
+ unsigned char hs2lp = 0, lp2hs = 0, hsfreq = 0;
+ unsigned int data;
+ void __iomem *base = info->reg_base;
+ int rate, rate_mhz, dsi_rate, lane_rate, 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, base + DSI_PHY_TMR_CFG);
+ /* set hsfreqrange */
+ hi3620_dsi_phy_write(base, 0x44, hsfreq);
+ writel_relaxed(0x7, base + DSI_PHY_RSTZ);
+
+#if 0
+ /* set tx esc clk division */
+ dsi_rate = clk_get_rate(info->clk_dsi);
+ lane_rate = dsi_rate / 4;
+ data = (lane_rate + (MAX_TX_ESC_CLK / 2) - 1) / MAX_TX_ESC_CLK;
+ writel_relaxed(data, base + DSI_CLKMGR_CFG);
+#endif
+}
+
+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 bool is_wfifo_full(void __iomem *base)
+{
+ unsigned int status;
+
+ status = readl_relaxed(base + DSI_CMD_PKT_STATUS);
+ if (status & GEN_PLD_W_FULL)
+ return true;
+ return false;
+}
+
+static int recv_generic_pkt(void __iomem *base, u8 *val, int len)
+{
+ int i, cnt;
+ unsigned int status, data;
+
+ cnt = (len + 3) / 4;
+ for (i = 0; i < cnt; i++) {
+ do {
+ cpu_relax();
+ status = readl_relaxed(base + DSI_CMD_PKT_STATUS);
+ if (status & GEN_PLD_R_EMPTY) {
+ pr_err("#%s, %d, found empty read fifo\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ } while (status & GEN_RD_CMD_BUSY);
+
+ data = readl_relaxed(base + DSI_GEN_PLD_DATA);
+ val[i * 4] = data & 0xff;
+ val[i * 4 + 1] = (data >> 8) & 0xff;
+ val[i * 4 + 2] = (data >> 16) & 0xff;
+ val[i * 4 + 3] = (data >> 24) & 0xff;
+ }
+ do {
+ cpu_relax();
+ status = readl_relaxed(base + DSI_CMD_PKT_STATUS);
+ /* read data until FIFO empty */
+ readl_relaxed(base + DSI_GEN_PLD_DATA);
+ } while (!(status & GEN_PLD_R_EMPTY));
+ return 0;
+}
+
+static void wait_phy_ready(void __iomem *base)
+{
+#if 0
+ unsigned int status;
+
+ do {
+ cpu_relax();
+ status = readl_relaxed(base + DSI_PHY_STATUS);
+ } while (!(status & DSI_PHY_STOP_STATE0_LANE));
+#else
+ /*
+ * In some board, it always loop as dead.
+ * So use delay as workaround.
+ */
+ msleep(5);
+#endif
+}
+
+int set_packet(struct device *dev, u8 *cmd, int nr_payload)
+{
+ struct hi3620fb_info *info = dev_get_drvdata(dev);
+ void __iomem *base = info->reg_base;
+ unsigned int header, type, data = 0;
+ int ret = 0, video_mode = 0, i = 0, j = 0;
+
+ mutex_lock(&info->dsi_mutex);
+ wait_phy_ready(base);
+ if (is_video_mode(base)) {
+ video_mode = 1;
+ set_cmd_mode(base);
+ }
+
+ /* set DT type */
+ type = cmd[0] & 0xff;
+ header = type;
+
+ switch (type) {
+ case DCS_SHORT_WR_PARAM0:
+ /* set DSI command */
+ header |= (cmd[1] & 0xff) << 8;
+ writel_relaxed(header, base + DSI_GEN_HDR);
+ break;
+ case DCS_SHORT_WR_PARAM1:
+ header |= (cmd[1] & 0xff) << 8;
+ header |= (cmd[2] & 0xff) << 16;
+ writel_relaxed(header, base + DSI_GEN_HDR);
+ break;
+ case GEN_LONG_WR:
+ for (i = 0; i < nr_payload;) {
+ data = 0;
+ for (j = 0; i < nr_payload && j < 4; i++, j++)
+ data |= cmd[i + 1] << (j * 8);
+ writel_relaxed(data, base + DSI_GEN_PLD_DATA);
+ if (is_wfifo_full(base))
+ pr_err("wfifo is full\n");
+ }
+ header |= nr_payload << 8;
+ writel_relaxed(header, base + DSI_GEN_HDR);
+ break;
+ case GEN_RD_PARAM1:
+ header |= (cmd[1] & 0xff) << 8;
+ writel_relaxed(header, base + DSI_GEN_HDR);
+ udelay(20); /* FIXME */
+ ret = recv_generic_pkt(base, &cmd[2], nr_payload);
+ break;
+ case DCS_RD_PARAM0:
+ header |= (cmd[1] & 0xff) << 8;
+ writel_relaxed(header, base + DSI_GEN_HDR);
+ udelay(20); /* FIXME */
+ ret = recv_generic_pkt(base, &cmd[2], 1);
+ break;
+ default:
+ pr_warn("###%s: not set packet type\n", __func__);
+ break;
+ }
+ wait_phy_ready(base);
+ /* restore video mode if necessary */
+ if (video_mode)
+ set_video_mode(base);
+ mutex_unlock(&info->dsi_mutex);
+ return ret;
+}
+
+static int write_packet(struct device *dev, enum DATA_TYPE type, u8 *cmd,
+ int len)
+{
+ struct hi3620fb_info *info = dev_get_drvdata(dev);
+ void __iomem *base = info->reg_base;
+ unsigned int header;
+ int ret, video_mode = 0;
+
+ if (is_video_mode(base)) {
+ video_mode = 1;
+ set_cmd_mode(base);
+ }
+
+ header = type & 0xff;
+ header |= (cmd[0] & 0xff) << 8;
+
+ switch (type) {
+ case DCS_SHORT_WR_PARAM0:
+ writel_relaxed(header, base + DSI_GEN_HDR);
+ break;
+ case DCS_SHORT_WR_PARAM1:
+ header |= (cmd[1] & 0xff) << 16;
+ writel_relaxed(header, base + DSI_GEN_HDR);
+ break;
+ default:
+ pr_err("#%s, %d, wrong type:%d\n", __func__, __LINE__, type);
+ break;
+ }
+ /* restore video mode if necessary */
+ if (video_mode)
+ set_video_mode(base);
+ return ret;
+}
+
+int dsi_set_packet(u8 *cmd, int nr_payload)
+{
+ if (dsi_dev)
+ return set_packet(dsi_dev, cmd, nr_payload);
+ else
+ return -EINVAL;
+}
+
+int hi3620_mipi_init(struct device *dev)
+{
+ struct hi3620fb_info *info = dev_get_drvdata(dev);
+
+ dsi_dev = dev;
+ mutex_init(&info->dsi_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(hi3620_mipi_init);
+
+int hi3620_mipi_enable(struct device *dev)
+{
+ struct hi3620fb_info *info = dev_get_drvdata(dev);
+
+ unreset(info->reg_base);
+ init_video_mode(info->reg_base, info);
+
+ clk_set_rate(info->clk_dsi, info->dsi_rate); /* huawei logo is shifted to right & color may be changed??? */
+ set_dpi_timing(info);
+ clk_prepare_enable(info->clk_dsi);
+
+ setup_phy(info);
+ enable_phy(info->reg_base);
+
+ set_cmd_mode(info->reg_base);
+ unset_highspeed(info->reg_base);
+
+ /* set panel on */
+
+ if (!strncmp(info->mipi_mode_name, "video", 5))
+ set_video_mode(info->reg_base);
+ else if (!strncmp(info->mipi_mode_name, "command", 7))
+ set_cmd_mode(info->reg_base);
+ else
+ return -EINVAL;
+ set_highspeed(info->reg_base);
+ return 0;
+}
+EXPORT_SYMBOL(hi3620_mipi_enable);
+
+int hi3620_mipi_disable(struct device *dev)
+{
+ struct hi3620fb_info *info = dev_get_drvdata(dev);
+
+ /* set panel off */
+
+ set_cmd_mode(info->reg_base);
+ unset_highspeed(info->reg_base);
+ unreset(info->reg_base);
+ disable_phy(info->reg_base);
+ reset(info->reg_base);
+ clk_disable_unprepare(info->clk_dsi);
+ return 0;
+}
+EXPORT_SYMBOL(hi3620_mipi_disable);
diff --git a/drivers/video/hisilicon/hi3620_fb.c b/drivers/video/hisilicon/hi3620_fb.c
new file mode 100644
index 000000000000..634ad1375b1f
--- /dev/null
+++ b/drivers/video/hisilicon/hi3620_fb.c
@@ -0,0 +1,926 @@
+/*
+ * 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/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/r63306.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_CTRL_CFG_OK, base + EDC_DISP_CTL);
+ data &= ~EDC_CTRL_CFG_OK;
+ writel_relaxed(data, base + EDC_DISP_CTL);
+}
+
+static void disable_edc(void __iomem *base)
+{
+ unsigned int data;
+
+ data = readl_relaxed(base + LDI_CTRL);
+ data &= ~(LDI_CTRL_EN | LDI_CTRL_SHUTDOWN);
+ writel_relaxed(data, base + LDI_CTRL);
+ data = readl_relaxed(base + EDC_DISP_CTL);
+ data &= ~EDC_CTRL_EN;
+ data |= EDC_CTRL_CLK_EN;
+ writel_relaxed(data, base + EDC_DISP_CTL);
+ update_edc(base);
+ data = readl_relaxed(base + EDC_VIDEO_CHAN_CTRL);
+ data &= ~EDC_VIDEO_CHAN_CTRL_EN;
+ writel_relaxed(data, base + EDC_VIDEO_CHAN_CTRL);
+}
+
+static void enable_edc(void __iomem *base)
+{
+ unsigned int data;
+
+ data = readl_relaxed(base + EDC_VIDEO_CHAN_CTRL);
+ data |= EDC_VIDEO_CHAN_CTRL_EN;
+ writel_relaxed(data, base + EDC_VIDEO_CHAN_CTRL);
+
+ data = readl_relaxed(base + LDI_WORK_MODE);
+ data |= LDI_WORK_MODE_EN;
+ writel_relaxed(data, base + LDI_WORK_MODE);
+
+ data = readl_relaxed(base + LDI_CTRL);
+ data &= ~(LDI_CTRL_SHUTDOWN | LDI_CTRL_COLOR_MODE | LDI_CTRL_BGR);
+ data &= ~(LDI_CTRL_DISP_MODE);
+ data |= LDI_CTRL_EN | LDI_CTRL_DATA_GATE_EN;
+ writel_relaxed(data, base + LDI_CTRL);
+
+ data = readl_relaxed(base + EDC_DISP_CTL);
+ data |= EDC_CTRL_EN | EDC_CTRL_CLK_EN;
+ data &= ~(EDC_CTRL_CFG_OK_SEL | EDC_CTRL_FRAME_END_START);
+ /* unflow level */
+ data &= ~(0xfff << 11);
+ data |= (0xc00 << 11);
+ writel_relaxed(data, base + EDC_DISP_CTL);
+ update_edc(base);
+}
+
+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 hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+ u32 data;
+
+ 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 = (var->yres - 1) & 0xfff;
+ data |= ((var->xres - 1) & 0xfff) << 16;
+ writel_relaxed(data, base + EDC_DISP_SIZE);
+
+ data = (fb->var.xres_virtual - 1) << 16 | (fb->var.yres_virtual - 1);
+ writel_relaxed(data, base + EDC_VIDEO_CHAN_SIZE);
+}
+
+static void set_graphics_start(struct fb_info *fb, int xoffs, int yoffs)
+{
+ struct hi3620fb_info *info = fb->par;
+ struct fb_var_screeninfo *var = &fb->var;
+ void __iomem *base = info->reg_base;
+ u32 addr, data;
+
+ if (yoffs >= var->yres)
+ data = (yoffs - var->yres) & 0xfff;
+ else
+ data = yoffs & 0xfff;
+ data |= (xoffs & 0xfff) << 16;
+ writel_relaxed(data, base + EDC_VIDEO_CHAN_XY);
+ /* setup dma address */
+ addr = (yoffs * var->xres_virtual + xoffs) * var->bits_per_pixel / 8;
+ addr = ALIGN(addr + fb->fix.smem_start, 64);
+ writel_relaxed(addr, 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;
+ unsigned int data;
+
+ /* setup dma stride */
+ writel_relaxed(fb->fix.line_length, base + EDC_VIDEO_CHAN_STRIDE);
+
+ /* setup outstanding */
+ data = readl_relaxed(base + EDC_DISP_CTL);
+ data &= ~(0xf << 26);
+ data |= (8 << 26);
+ writel_relaxed(data, base + EDC_DISP_CTL);
+}
+
+static void set_color(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;
+
+ 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;
+ }
+ writel_relaxed(ctrl, base + EDC_VIDEO_CHAN_CTRL);
+ ctrl = readl_relaxed(base + EDC_DISP_CTL);
+ ctrl &= ~(3 << 6);
+ switch (info->pix_fmt) {
+ case IMG_PIXEL_FORMAT_ARGB1555:
+ case IMG_PIXEL_FORMAT_RGB555:
+ case IMG_PIXEL_FORMAT_RGB565:
+ break;
+ case IMG_PIXEL_FORMAT_RGB888:
+ case IMG_PIXEL_FORMAT_ARGB8888:
+ ctrl |= 2 << 6;
+ break;
+ }
+ writel_relaxed(ctrl, base + EDC_DISP_CTL);
+ ctrl = readl_relaxed(base + LDI_CTRL);
+ ctrl &= ~(3 << 3);
+ switch (info->pix_fmt) {
+ case IMG_PIXEL_FORMAT_ARGB1555:
+ case IMG_PIXEL_FORMAT_RGB555:
+ case IMG_PIXEL_FORMAT_RGB565:
+ break;
+ case IMG_PIXEL_FORMAT_RGB888:
+ case IMG_PIXEL_FORMAT_ARGB8888:
+ ctrl |= 2 << 3;
+ break;
+ }
+ writel_relaxed(ctrl, base + LDI_CTRL);
+ ctrl = readl_relaxed(base + DSI_DPI_CFG);
+ ctrl &= ~(7 << 2);
+ switch (info->pix_fmt) {
+ case IMG_PIXEL_FORMAT_ARGB1555:
+ case IMG_PIXEL_FORMAT_RGB555:
+ case IMG_PIXEL_FORMAT_RGB565:
+ break;
+ case IMG_PIXEL_FORMAT_RGB888:
+ case IMG_PIXEL_FORMAT_ARGB8888:
+ ctrl |= 5 << 2;
+ break;
+ }
+ writel_relaxed(ctrl, base + DSI_DPI_CFG);
+}
+
+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);
+ ctrl |= EDC_VIDEO_CHAN_CTRL_EN;
+ writel_relaxed(ctrl, base + EDC_VIDEO_CHAN_CTRL);
+
+ set_color(fb);
+ 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)
+{
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+
+ set_graphics_start(fb, var->xoffset, var->yoffset);
+ update_edc(base);
+ return 0;
+}
+
+static int hi3620fb_blank(int blank_mode, struct fb_info *info)
+{
+ return 0;
+}
+
+static int hi3620fb_ioctl(struct fb_info *fb, unsigned int cmd,
+ unsigned long arg)
+{
+ struct hi3620fb_info *info = fb->par;
+ u32 count = 0, status = 0, mask = 0;
+ int ret = 0;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ count = info->vsync_cnt;
+
+ /* clear interrupt status of End of Frame */
+ status = readl_relaxed(info->reg_base + EDC_INTS);
+ status &= ~EDC_INT_BAS_END;
+ writel_relaxed(status, info->reg_base + EDC_INTS);
+ /* unmask interrupt of End of Frame */
+ mask = readl_relaxed(info->reg_base + EDC_INTE);
+ mask &= ~EDC_INT_BAS_END;
+ writel_relaxed(mask, info->reg_base + EDC_INTE);
+
+ ret = wait_event_interruptible_timeout(info->wait_vsync,
+ count != info->vsync_cnt,
+ HZ / 10);
+ if (ret < 0)
+ return ret;
+ /* mask interrupt of End of Frame */
+ mask |= EDC_INT_BAS_END;
+ writel_relaxed(mask, info->reg_base + EDC_INTE);
+ break;
+ }
+ 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,
+ .fb_blank = hi3620fb_blank,
+ .fb_ioctl = hi3620fb_ioctl,
+ .fb_compat_ioctl = hi3620fb_ioctl,
+};
+
+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 * 2; /* double buffering */
+ 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 = length;
+ 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;
+ u32 status;
+
+ /* clear masked interrupts */
+ status = readl_relaxed(info->reg_base + EDC_INTS);
+ status &= ~readl_relaxed(info->reg_base + EDC_INTE) & 0x3ffff;
+
+ if (status & EDC_INT_BAS_END) {
+ status &= ~EDC_INT_BAS_END;
+ info->vsync_cnt++;
+ wake_up_interruptible(&info->wait_vsync);
+ }
+ writel_relaxed(status, info->reg_base + EDC_INTS);
+ 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;
+ }
+ 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);
+ if (!fb->screen_base) {
+ dev_err(dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+ 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
+ platform_set_drvdata(pdev, info);
+
+ disable_edc(info->reg_base);
+ hi3620_mipi_init(dev);
+ hi3620_mipi_enable(dev);
+ set_graphics_start(fb, 0, 0);
+ hi3620fb_set_par(fb);
+
+
+ /* clear IRQ status & enable IRQ */
+ writel_relaxed(0, info->reg_base + EDC_INTS);
+ /* enable interrupts of bus error */
+ writel_relaxed(0x2ff, 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);
+
+ enable_edc(info->reg_base);
+
+ 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;
+ }
+#if 0
+ 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;
+ }
+#endif
+ ret = register_framebuffer(fb);
+ if (ret < 0) {
+ dev_err(dev, "failed to register hi3620 framebuffer\n");
+ goto err_clk;
+ }
+ init_waitqueue_head(&info->wait_vsync);
+ /* 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)
+{
+ hi3620_mipi_disable(&pdev->dev);
+ return 0;
+}
+
+static void dump_reg(void __iomem *base, int offset, int size)
+{
+ int i;
+ for (i = 0; i < size; i += 4) {
+ pr_err("#[0x%x]:0x%x\n", (int)base + offset + i, readl_relaxed(base + offset + i));
+ }
+}
+
+static int dsi_init(void __iomem *base)
+{
+ writel_relaxed(1, base + DSI_PWR_UP);
+ writel_relaxed(7, base + DSI_PHY_RSTZ);
+ return 0;
+}
+
+static int hi3620_fb_suspend(struct device *dev)
+{
+ unsigned int data;
+ struct hi3620fb_info *info = dev_get_drvdata(dev);
+ void __iomem *base = info->reg_base;
+
+ dsi_enter_sleep();
+
+ info->graph_chan_addr = readl_relaxed(base + EDC_GRAPH_CHAN_ADDR);
+ info->video_chan_addr = readl_relaxed(base + EDC_VIDEO_CHAN_ADDR);
+ info->graph_chan_stride = readl_relaxed(base + EDC_GRAPH_CHAN_STRIDE);
+ info->video_chan_stride = readl_relaxed(base + EDC_VIDEO_CHAN_STRIDE);
+ info->graph_chan_xy = readl_relaxed(base + EDC_GRAPH_CHAN_XY);
+ info->video_chan_xy = readl_relaxed(base + EDC_VIDEO_CHAN_XY);
+ info->graph_chan_size = readl_relaxed(base + EDC_GRAPH_CHAN_SIZE);
+ info->video_chan_size = readl_relaxed(base + EDC_VIDEO_CHAN_SIZE);
+
+ info->edc_disp_size = readl_relaxed(base + EDC_DISP_SIZE);
+ info->edc_disp_dpd = readl_relaxed(base + EDC_DISP_DPD);
+ info->ldi_hrz_ctrl0 = readl_relaxed(base + LDI_HRZ_CTRL0);
+ info->ldi_hrz_ctrl1 = readl_relaxed(base + LDI_HRZ_CTRL1);
+ info->ldi_vrt_ctrl0 = readl_relaxed(base + LDI_VRT_CTRL0);
+ info->ldi_vrt_ctrl1 = readl_relaxed(base + LDI_VRT_CTRL1);
+ info->ldi_plr_ctrl = readl_relaxed(base + LDI_PLR_CTRL);
+ info->ldi_dsp_size = readl_relaxed(base + LDI_DSP_SIZE);
+ info->ldi_ctrl = readl_relaxed(base + LDI_CTRL);
+ info->ldi_inte = readl_relaxed(base + LDI_INT_EN);
+ info->dsi_cmd_mod_ctrl = readl_relaxed(base + DSI_CMD_MOD_CTRL);
+ info->dsi_te_ctrl = readl_relaxed(base + DSI_TE_CTRL);
+ info->dsi_te_hs_num = readl_relaxed(base + DSI_TE_HS_NUM);
+ info->dsi_te_hs_wd = readl_relaxed(base + DSI_TE_HS_WD);
+ info->dsi_te_vs_wd = readl_relaxed(base + DSI_TE_VS_WD);
+ info->clkmgr_cfg = readl_relaxed(base + DSI_CLKMGR_CFG);
+ info->dpi_cfg = readl_relaxed(base + DSI_DPI_CFG);
+ info->pckhdl_cfg = readl_relaxed(base + DSI_PCKHDL_CFG);
+ info->vid_mode_cfg = readl_relaxed(base + DSI_VID_MODE_CFG);
+ info->vid_pkt_cfg = readl_relaxed(base + DSI_VID_PKT_CFG);
+ info->cmd_mode_cfg = readl_relaxed(base + DSI_CMD_MODE_CFG);
+ info->tmr_line_cfg = readl_relaxed(base + DSI_TMR_LINE_CFG);
+ info->vtiming_cfg = readl_relaxed(base + DSI_VTIMING_CFG);
+ info->to_cnt_cfg = readl_relaxed(base + DSI_TO_CNT_CFG);
+ info->phy_if_cfg = readl_relaxed(base + DSI_PHY_IF_CFG);
+ info->phy_if_ctrl = readl_relaxed(base + DSI_PHY_IF_CTRL);
+ info->edpi_cfg = readl_relaxed(base + DSI_EDPI_CFG);
+ info->lpcmd_time = readl_relaxed(base + DSI_LPCMD_TIME);
+
+ /* disable channel */
+ data = readl_relaxed(base + EDC_GRAPH_CHAN_CTRL);
+ info->graph_chan_ctrl = data;
+ data &= ~EDC_GRAPH_CHAN_CTRL_EN;
+ writel_relaxed(data, base + EDC_GRAPH_CHAN_CTRL);
+ data = readl_relaxed(base + EDC_VIDEO_CHAN_CTRL);
+ info->video_chan_ctrl = data;
+ data &= ~EDC_VIDEO_CHAN_CTRL_EN;
+ writel_relaxed(data, base + EDC_VIDEO_CHAN_CTRL);
+
+ /* disable all EDC interrupts */
+ info->edc_inte = readl_relaxed(base + EDC_INTE);
+ writel_relaxed(0xffffffff, base + EDC_INTE);
+ /* clear EDC interrupt status */
+ writel_relaxed(0, base + EDC_INTS);
+
+ /* disable EDC */
+ data = readl_relaxed(base + EDC_DISP_CTL);
+ info->edc_disp_ctrl = data;
+ data &= ~EDC_CTRL_EN;
+ writel_relaxed(data, base + EDC_DISP_CTL);
+ update_edc(base);
+ return 0;
+}
+
+static int hi3620_fb_resume(struct device *dev)
+{
+ struct hi3620fb_info *info = dev_get_drvdata(dev);
+ void __iomem *base = info->reg_base;
+
+ dsi_init(base);
+ hi3620_mipi_enable(info->dev);
+ writel_relaxed(info->clkmgr_cfg, base + DSI_CLKMGR_CFG);
+ set_graphics_start(info->fb, 0, 0);
+ hi3620fb_set_par(info->fb);
+ writel_relaxed(info->ldi_inte, base + LDI_INT_EN);
+ writel_relaxed(0x3fff, info->reg_base + LDI_INT_CLR);
+
+ /* clear EDC interrupt status */
+ writel_relaxed(0, base + EDC_INTS);
+ /* enable all EDC interrupts */
+ writel_relaxed(info->edc_inte, base + EDC_INTE);
+
+ writel_relaxed(info->ldi_ctrl, base + LDI_CTRL);
+
+ enable_edc(base);
+
+ /* enable channel */
+ writel_relaxed(info->graph_chan_ctrl,
+ base + EDC_GRAPH_CHAN_CTRL);
+ writel_relaxed(info->video_chan_ctrl,
+ base + EDC_VIDEO_CHAN_CTRL);
+
+ dsi_exit_sleep();
+
+ return 0;
+}
+
+static const struct dev_pm_ops hi3620_fb_pm_ops = {
+ .suspend = hi3620_fb_suspend,
+ .resume = hi3620_fb_resume,
+};
+
+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,
+ .pm = &hi3620_fb_pm_ops,
+ .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..213d0695a8ab
--- /dev/null
+++ b/drivers/video/hisilicon/hi3620_fb.h
@@ -0,0 +1,208 @@
+#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_SIZE 0x090
+#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_CMD_PKT_STATUS 0x93c
+#define DSI_TO_CNT_CFG 0x940
+#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_STATUS 0x960
+#define DSI_PHY_TST_CTRL0 0x964
+#define DSI_PHY_TST_CTRL1 0x968
+#define DSI_EDPI_CFG 0x96c
+#define DSI_LPCMD_TIME 0x970
+
+/* bits field of DSI_CMD_PKT_STATUS register */
+#define GEN_RD_CMD_BUSY (1 << 6)
+#define GEN_PLD_R_FULL (1 << 5)
+#define GEN_PLD_R_EMPTY (1 << 4)
+#define GEN_PLD_W_FULL (1 << 3)
+
+/* bits field of EDC_DISP_CTL register */
+#define EDC_CTRL_CLK_EN (1 << 31)
+#define EDC_CTRL_FRAME_END_START (1 << 30)
+#define EDC_CTRL_EN (1 << 10)
+#define EDC_CTRL_CFG_OK (1 << 1)
+#define EDC_CTRL_CFG_OK_SEL (1 << 0)
+
+/* bits field of EDC_INTS/EDC_INTE register */
+#define EDC_INT_BAS_END (1 << 6)
+
+/* bits field of LDI_CTRL register */
+#define LDI_CTRL_SHUTDOWN (1 << 15)
+#define LDI_CTRL_COLOR_MODE (1 << 14)
+#define LDI_CTRL_BGR (1 << 13)
+#define LDI_CTRL_DATA_GATE_EN (1 << 2)
+#define LDI_CTRL_DISP_MODE (1 << 1)
+#define LDI_CTRL_EN (1 << 0)
+
+/* bits field of LDI_WORK_MODE register */
+#define LDI_WORK_MODE_EN (1 << 0)
+
+/* 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)
+
+/* bits field of DSI_PHY_STATUS register */
+#define DSI_PHY_STOP_STATE3_LANE (1 << 12)
+#define DSI_PHY_STOP_STATE2_LANE (1 << 9)
+#define DSI_PHY_STOP_STATE1_LANE (1 << 7)
+#define DSI_PHY_STOP_STATE0_LANE (1 << 4)
+#define DSI_PHY_LOCK (1 << 0)
+
+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;
+ 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;
+ wait_queue_head_t wait_vsync;
+ int vsync_cnt;
+ struct mutex dsi_mutex;
+ unsigned int graph_chan_addr;
+ unsigned int graph_chan_stride;
+ unsigned int graph_chan_xy;
+ unsigned int graph_chan_size;
+ unsigned int graph_chan_ctrl;
+ unsigned int video_chan_addr;
+ unsigned int video_chan_stride;
+ unsigned int video_chan_xy;
+ unsigned int video_chan_size;
+ unsigned int video_chan_ctrl;
+ unsigned int edc_inte;
+ unsigned int edc_disp_size;
+ unsigned int edc_disp_dpd;
+ unsigned int edc_disp_ctrl;
+ unsigned int ldi_hrz_ctrl0;
+ unsigned int ldi_hrz_ctrl1;
+ unsigned int ldi_vrt_ctrl0;
+ unsigned int ldi_vrt_ctrl1;
+ unsigned int ldi_plr_ctrl;
+ unsigned int ldi_dsp_size;
+ unsigned int ldi_inte;
+ unsigned int ldi_ctrl;
+ unsigned int ldi_work_mode;
+ unsigned int ldi_hdmi_dsi_gt;
+ unsigned int dsi_cmd_mod_ctrl;
+ unsigned int dsi_te_ctrl;
+ unsigned int dsi_te_hs_num;
+ unsigned int dsi_te_hs_wd;
+ unsigned int dsi_te_vs_wd;
+ unsigned int clkmgr_cfg;
+ unsigned int dpi_cfg;
+ unsigned int pckhdl_cfg;
+ unsigned int vid_mode_cfg;
+ unsigned int vid_pkt_cfg;
+ unsigned int cmd_mode_cfg;
+ unsigned int tmr_line_cfg;
+ unsigned int vtiming_cfg;
+ unsigned int to_cnt_cfg;
+ unsigned int phy_rstz;
+ unsigned int phy_if_cfg;
+ unsigned int phy_if_ctrl;
+ unsigned int edpi_cfg;
+ unsigned int lpcmd_time;
+};
+
+extern int hi3620_mipi_init(struct device *dev);
+extern int hi3620_mipi_enable(struct device *dev);
+extern int hi3620_mipi_disable(struct device *dev);
+extern int send_generic_packet(u8 *cmd, int len);
+#endif /* __HI3620FB_H */
diff --git a/include/linux/mfd/r63306.h b/include/linux/mfd/r63306.h
new file mode 100644
index 000000000000..c266c99966ad
--- /dev/null
+++ b/include/linux/mfd/r63306.h
@@ -0,0 +1,45 @@
+
+#ifndef __R63306_H
+#define __R63306_H
+
+struct r63306_chip {
+ struct device *dev;
+ struct mutex *lock;
+};
+
+struct r63306_pwm {
+ int divider;
+ int duty_cycle;
+ int pwm_wm;
+};
+
+struct r63306_device_id {
+ int vendor;
+ int product;
+ int revision;
+};
+
+struct r63306_cabc {
+ bool cabc_on;
+ bool pwm_on;
+ bool pfm_on;
+ bool ledpwm_pin;
+ bool ledpwm_pol;
+};
+
+struct r63306_cabc_param {
+ int backlight;
+};
+
+extern int dsi_reset(void);
+extern int dsi_enter_sleep(void);
+extern int dsi_exit_sleep(void);
+extern int dsi_get_pwm(struct r63306_pwm *pwm);
+extern int dsi_set_pwm(struct r63306_pwm *pwm);
+extern int dsi_get_cabc(struct r63306_cabc *cabc);
+extern int dsi_set_cabc(struct r63306_cabc *cabc);
+extern int dsi_get_cabc_param(struct r63306_cabc_param *cabc);
+extern int dsi_set_cabc_param(struct r63306_cabc_param *cabc);
+extern int dsi_get_backlight(int *level);
+
+#endif /* __R63306_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..ebc141cf482a
--- /dev/null
+++ b/include/linux/platform_data/hi3620-dsi.h
@@ -0,0 +1,53 @@
+/*
+ * 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
+
+enum DATA_TYPE {
+ GEN_SHORT_WR_PARAM0 = 0x03,
+ GEN_SHORT_WR_PARAM1 = 0x13,
+ GEN_SHORT_WR_PARAM2 = 0x23,
+ GEN_RD_PARAM0 = 0x04,
+ GEN_RD_PARAM1 = 0x14,
+ GEN_RD_PARAM2 = 0x24,
+ DCS_SHORT_WR_PARAM0 = 0x05,
+ DCS_SHORT_WR_PARAM1 = 0x15,
+ DCS_RD_PARAM0 = 0x06,
+ SET_MAX_PKT = 0x37,
+ NULL_PKT = 0x09,
+ BLANKING_PKT = 0x19,
+ GEN_LONG_WR = 0x29,
+ DCS_LONG_WR = 0x39,
+ PACKED_PIXEL_16B = 0x0e,
+ PACKED_PIXEL_18B = 0x1e,
+ LOOSELY_PACKED_PIXEL_18B = 0x2e,
+ PACKED_PIXEL_24B = 0x3e,
+};
+
+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
diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h
index fb795c3b3c17..864e01e9613c 100644
--- a/include/uapi/linux/fb.h
+++ b/include/uapi/linux/fb.h
@@ -226,6 +226,11 @@ struct fb_bitfield {
#define FB_VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */
#define FB_VMODE_CONUPDATE 512 /* don't update x/yoffset */
+#define FB_FLAG_DE_HIGH 1 /* data enable high active */
+#define FB_FLAG_DE_LOW 2 /* data enable low active */
+#define FB_FLAG_PIXDATA_POSEDGE 4 /* pixdata postive edge */
+#define FB_FLAG_PIXDATA_NEGEDGE 8 /* pixdata negative edge */
+
/*
* Display rotation support
*/