diff options
author | Guodong Xu <guodong.xu@linaro.org> | 2013-10-24 11:53:42 +0800 |
---|---|---|
committer | Guodong Xu <guodong.xu@linaro.org> | 2013-10-24 11:53:42 +0800 |
commit | d1b7fb8d9cf8bad1ae921655457cb31c0b8292e2 (patch) | |
tree | 06530c3c3ea69499eb2bab1d5ed87ec9fbbad488 | |
parent | 0ec66e599589c8600ee53c51df9794953d508651 (diff) | |
parent | fcba55f3f98726c20d2424a055372c6b5e669b5e (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_defconfig | 13 | ||||
-rw-r--r-- | drivers/clk/hisilicon/Kconfig | 4 | ||||
-rw-r--r-- | drivers/clk/hisilicon/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clk-hi3620-dsi.c | 364 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/r63306.c | 327 | ||||
-rw-r--r-- | drivers/video/Kconfig | 1 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/fbmon.c | 10 | ||||
-rw-r--r-- | drivers/video/hisilicon/Kconfig | 11 | ||||
-rw-r--r-- | drivers/video/hisilicon/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/hisilicon/hi3620_dsi.c | 626 | ||||
-rw-r--r-- | drivers/video/hisilicon/hi3620_fb.c | 926 | ||||
-rw-r--r-- | drivers/video/hisilicon/hi3620_fb.h | 208 | ||||
-rw-r--r-- | include/linux/mfd/r63306.h | 45 | ||||
-rw-r--r-- | include/linux/platform_data/hi3620-dsi.h | 53 | ||||
-rw-r--r-- | include/uapi/linux/fb.h | 5 |
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 */ |