diff --git a/patch/u-boot/u-boot-sunxi/01-add-sunxi-pwm-driver.patch b/patch/u-boot/u-boot-sunxi/01-add-sunxi-pwm-driver.patch new file mode 100644 index 000000000..45cc18247 --- /dev/null +++ b/patch/u-boot/u-boot-sunxi/01-add-sunxi-pwm-driver.patch @@ -0,0 +1,274 @@ +From 485a09477c96edda8dd737493efd6f6e03ecf06e Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Sun, 17 Sep 2017 09:19:52 -0700 +Subject: [PATCH] pwm: sunxi: add support for PWM found on Allwinner A64 and H3 + +This commit adds basic support for PWM found on Allwinner A64 and H3 +It can be used for pwm_backlight driver (e.g. for Pinebook) + +Signed-off-by: Vasily Khoruzhick +--- + arch/arm/include/asm/arch-sunxi/gpio.h | 1 + + arch/arm/include/asm/arch-sunxi/pwm.h | 12 +++ + drivers/pwm/Kconfig | 7 ++ + drivers/pwm/Makefile | 1 + + drivers/pwm/sunxi_pwm.c | 184 +++++++++++++++++++++++++++++++++ + 5 files changed, 205 insertions(+) + create mode 100644 drivers/pwm/sunxi_pwm.c + +diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h +index 24f85206c8..7265d18099 100644 +--- a/arch/arm/include/asm/arch-sunxi/gpio.h ++++ b/arch/arm/include/asm/arch-sunxi/gpio.h +@@ -173,6 +173,7 @@ enum sunxi_gpio_number { + #define SUN8I_GPD_SDC1 3 + #define SUNXI_GPD_LCD0 2 + #define SUNXI_GPD_LVDS0 3 ++#define SUNXI_GPD_PWM 2 + + #define SUN5I_GPE_SDC2 3 + #define SUN8I_GPE_TWI2 3 +diff --git a/arch/arm/include/asm/arch-sunxi/pwm.h b/arch/arm/include/asm/arch-sunxi/pwm.h +index 5884b5dbe7..673e0eb7b5 100644 +--- a/arch/arm/include/asm/arch-sunxi/pwm.h ++++ b/arch/arm/include/asm/arch-sunxi/pwm.h +@@ -11,8 +11,15 @@ + #define SUNXI_PWM_CH0_PERIOD (SUNXI_PWM_BASE + 4) + + #define SUNXI_PWM_CTRL_PRESCALE0(x) ((x) & 0xf) ++#define SUNXI_PWM_CTRL_PRESCALE0_MASK (0xf) + #define SUNXI_PWM_CTRL_ENABLE0 (0x5 << 4) + #define SUNXI_PWM_CTRL_POLARITY0(x) ((x) << 5) ++#define SUNXI_PWM_CTRL_POLARITY0_MASK (1 << 5) ++#define SUNXI_PWM_CTRL_CLK_GATE (1 << 6) ++ ++#define SUNXI_PWM_CH0_PERIOD_MAX (0xffff) ++#define SUNXI_PWM_CH0_PERIOD_PRD(x) ((x & 0xffff) << 16) ++#define SUNXI_PWM_CH0_PERIOD_DUTY(x) ((x) & 0xffff) + + #define SUNXI_PWM_PERIOD_80PCT 0x04af03c0 + +@@ -31,4 +38,9 @@ + #define SUNXI_PWM_MUX SUN8I_GPH_PWM + #endif + ++struct sunxi_pwm { ++ u32 ctrl; ++ u32 ch0_period; ++}; ++ + #endif +diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig +index e827558052..2984b79766 100644 +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -43,3 +43,10 @@ config PWM_TEGRA + four channels with a programmable period and duty cycle. Only a + 32KHz clock is supported by the driver but the duty cycle is + configurable. ++ ++config PWM_SUNXI ++ bool "Enable support for the Allwinner Sunxi PWM" ++ depends on DM_PWM ++ help ++ This PWM is found on H3, A64 and other Allwinner SoCs. It supports a ++ programmable period and duty cycle. A 16-bit counter is used. +diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile +index 29d59916cb..1a8f8a58bc 100644 +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -17,3 +17,4 @@ obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o + obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o + obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o + obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o ++obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o +diff --git a/drivers/pwm/sunxi_pwm.c b/drivers/pwm/sunxi_pwm.c +new file mode 100644 +index 0000000000..cfea7d69f3 +--- /dev/null ++++ b/drivers/pwm/sunxi_pwm.c +@@ -0,0 +1,184 @@ ++/* ++ * Copyright (c) 2017 Vasily Khoruzhick ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++struct sunxi_pwm_priv { ++ struct sunxi_pwm *regs; ++ ulong freq; ++ bool invert; ++ uint32_t prescaler; ++}; ++ ++static const uint32_t prescaler_table[] = { ++ 120, /* 0000 */ ++ 180, /* 0001 */ ++ 240, /* 0010 */ ++ 360, /* 0011 */ ++ 480, /* 0100 */ ++ 0, /* 0101 */ ++ 0, /* 0110 */ ++ 0, /* 0111 */ ++ 12000, /* 1000 */ ++ 24000, /* 1001 */ ++ 36000, /* 1010 */ ++ 48000, /* 1011 */ ++ 72000, /* 1100 */ ++ 0, /* 1101 */ ++ 0, /* 1110 */ ++ 1, /* 1111 */ ++}; ++ ++static const uint64_t nsecs_per_sec = 1000000000L; ++ ++static int sunxi_pwm_config_pinmux(void) ++{ ++#ifdef CONFIG_MACH_SUN50I ++ sunxi_gpio_set_cfgpin(SUNXI_GPD(22), SUNXI_GPD_PWM); ++#endif ++ return 0; ++} ++ ++static int sunxi_pwm_set_invert(struct udevice *dev, uint channel, bool polarity) ++{ ++ struct sunxi_pwm_priv *priv = dev_get_priv(dev); ++ ++ debug("%s: polarity=%u\n", __func__, polarity); ++ priv->invert = polarity; ++ ++ return 0; ++} ++ ++static int sunxi_pwm_set_config(struct udevice *dev, uint channel, uint period_ns, ++ uint duty_ns) ++{ ++ struct sunxi_pwm_priv *priv = dev_get_priv(dev); ++ struct sunxi_pwm *regs = priv->regs; ++ int prescaler; ++ u32 v, period, duty; ++ uint64_t div = 0, pval = 0, scaled_freq = 0; ++ ++ debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns); ++ ++ for (prescaler = 0; prescaler < SUNXI_PWM_CTRL_PRESCALE0_MASK; prescaler++) { ++ if (!prescaler_table[prescaler]) ++ continue; ++ div = priv->freq; ++ pval = prescaler_table[prescaler]; ++ scaled_freq = lldiv(div, pval); ++ div = scaled_freq * period_ns; ++ div = lldiv(div, nsecs_per_sec); ++ if (div - 1 <= SUNXI_PWM_CH0_PERIOD_MAX) ++ break; ++ } ++ ++ if (div - 1 > SUNXI_PWM_CH0_PERIOD_MAX) { ++ debug("%s: failed to find prescaler value\n", __func__); ++ return -EINVAL; ++ } ++ ++ period = div; ++ div = scaled_freq * duty_ns; ++ div = lldiv(div, nsecs_per_sec); ++ duty = div; ++ ++ if (priv->prescaler != prescaler) { ++ /* Mask clock to update prescaler */ ++ v = readl(®s->ctrl); ++ v &= ~SUNXI_PWM_CTRL_CLK_GATE; ++ writel(v, ®s->ctrl); ++ v &= ~SUNXI_PWM_CTRL_PRESCALE0_MASK; ++ v |= (priv->prescaler & SUNXI_PWM_CTRL_PRESCALE0_MASK); ++ writel(v, ®s->ctrl); ++ v |= SUNXI_PWM_CTRL_CLK_GATE; ++ writel(v, ®s->ctrl); ++ priv->prescaler = prescaler; ++ } ++ ++ writel(SUNXI_PWM_CH0_PERIOD_PRD(period) | ++ SUNXI_PWM_CH0_PERIOD_DUTY(duty), ®s->ch0_period); ++ ++ debug("%s: prescaler: %d, period: %d, duty: %d\n", __func__, priv->prescaler, ++ period, duty); ++ ++ return 0; ++} ++ ++static int sunxi_pwm_set_enable(struct udevice *dev, uint channel, bool enable) ++{ ++ struct sunxi_pwm_priv *priv = dev_get_priv(dev); ++ struct sunxi_pwm *regs = priv->regs; ++ uint32_t v; ++ ++ debug("%s: Enable '%s'\n", __func__, dev->name); ++ ++ v = readl(®s->ctrl); ++ if (!enable) { ++ v &= ~SUNXI_PWM_CTRL_ENABLE0; ++ writel(v, ®s->ctrl); ++ return 0; ++ } ++ ++ sunxi_pwm_config_pinmux(); ++ ++ v &= ~SUNXI_PWM_CTRL_POLARITY0_MASK; ++ v |= priv->invert ? SUNXI_PWM_CTRL_POLARITY0(0) : ++ SUNXI_PWM_CTRL_POLARITY0(1); ++ v |= SUNXI_PWM_CTRL_ENABLE0; ++ writel(v, ®s->ctrl); ++ ++ return 0; ++} ++ ++static int sunxi_pwm_ofdata_to_platdata(struct udevice *dev) ++{ ++ struct sunxi_pwm_priv *priv = dev_get_priv(dev); ++ ++ priv->regs = (struct sunxi_pwm *)devfdt_get_addr(dev); ++ ++ return 0; ++} ++ ++static int sunxi_pwm_probe(struct udevice *dev) ++{ ++ struct sunxi_pwm_priv *priv = dev_get_priv(dev); ++ ++ priv->freq = 24000000; ++ ++ return 0; ++} ++ ++static const struct pwm_ops sunxi_pwm_ops = { ++ .set_invert = sunxi_pwm_set_invert, ++ .set_config = sunxi_pwm_set_config, ++ .set_enable = sunxi_pwm_set_enable, ++}; ++ ++static const struct udevice_id sunxi_pwm_ids[] = { ++ { .compatible = "allwinner,sun8i-h3-pwm" }, ++ { } ++}; ++ ++U_BOOT_DRIVER(sunxi_pwm) = { ++ .name = "sunxi_pwm", ++ .id = UCLASS_PWM, ++ .of_match = sunxi_pwm_ids, ++ .ops = &sunxi_pwm_ops, ++ .ofdata_to_platdata = sunxi_pwm_ofdata_to_platdata, ++ .probe = sunxi_pwm_probe, ++ .priv_auto_alloc_size = sizeof(struct sunxi_pwm_priv), ++}; diff --git a/patch/u-boot/u-boot-sunxi/02-add-axp803-pmic-support.patch b/patch/u-boot/u-boot-sunxi/02-add-axp803-pmic-support.patch new file mode 100644 index 000000000..5a152f993 --- /dev/null +++ b/patch/u-boot/u-boot-sunxi/02-add-axp803-pmic-support.patch @@ -0,0 +1,821 @@ +From 15d68b2115b24dcf3a20f67942db867e5887b624 Mon Sep 17 00:00:00 2001 +From: Icenowy Zheng +Date: Mon, 27 Mar 2017 19:22:34 +0200 +Subject: [PATCH] sunxi: add AXP803 support + +The A64 uses the AXP803 as its PMIC. + +Signed-off-by: Icenowy Zheng +Signed-off-by: Jernej Skrabec +--- + arch/arm/mach-sunxi/Makefile | 3 + + arch/arm/mach-sunxi/pmic_bus.c | 6 +- + arch/arm/mach-sunxi/rsb.c | 2 +- + board/sunxi/board.c | 31 ++--- + drivers/power/Kconfig | 85 +++++++++----- + drivers/power/Makefile | 1 + + drivers/power/axp803.c | 259 +++++++++++++++++++++++++++++++++++++++++ + drivers/power/axp818.c | 2 +- + include/axp803.h | 73 ++++++++++++ + include/axp_pmic.h | 3 + + 10 files changed, 415 insertions(+), 50 deletions(-) + create mode 100644 drivers/power/axp803.c + create mode 100644 include/axp803.h + +diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile +index 2a3c379b72..0bbfda9364 100644 +--- a/arch/arm/mach-sunxi/Makefile ++++ b/arch/arm/mach-sunxi/Makefile +@@ -19,9 +19,11 @@ endif + obj-$(CONFIG_MACH_SUN6I) += prcm.o + obj-$(CONFIG_MACH_SUN8I) += prcm.o + obj-$(CONFIG_MACH_SUN9I) += prcm.o ++obj-$(CONFIG_MACH_SUN50I) += prcm.o + obj-$(CONFIG_MACH_SUN6I) += p2wi.o + obj-$(CONFIG_MACH_SUN8I) += rsb.o + obj-$(CONFIG_MACH_SUN9I) += rsb.o ++obj-$(CONFIG_MACH_SUN50I) += rsb.o + obj-$(CONFIG_MACH_SUN4I) += clock_sun4i.o + obj-$(CONFIG_MACH_SUN5I) += clock_sun4i.o + obj-$(CONFIG_MACH_SUN6I) += clock_sun6i.o +@@ -37,6 +39,7 @@ obj-$(CONFIG_MACH_SUN9I) += clock_sun9i.o gtbus_sun9i.o + obj-$(CONFIG_AXP152_POWER) += pmic_bus.o + obj-$(CONFIG_AXP209_POWER) += pmic_bus.o + obj-$(CONFIG_AXP221_POWER) += pmic_bus.o ++obj-$(CONFIG_AXP803_POWER) += pmic_bus.o + obj-$(CONFIG_AXP809_POWER) += pmic_bus.o + obj-$(CONFIG_AXP818_POWER) += pmic_bus.o + +diff --git a/arch/arm/mach-sunxi/pmic_bus.c b/arch/arm/mach-sunxi/pmic_bus.c +index f917c3e070..e021b58b60 100644 +--- a/arch/arm/mach-sunxi/pmic_bus.c ++++ b/arch/arm/mach-sunxi/pmic_bus.c +@@ -36,7 +36,7 @@ int pmic_bus_init(void) + if (!needs_init) + return 0; + +-#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER ++#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP803_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER + # ifdef CONFIG_MACH_SUN6I + p2wi_init(); + ret = p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, AXP221_CTRL_ADDR, +@@ -65,7 +65,7 @@ int pmic_bus_read(u8 reg, u8 *data) + return i2c_read(AXP152_I2C_ADDR, reg, 1, data, 1); + #elif defined CONFIG_AXP209_POWER + return i2c_read(AXP209_I2C_ADDR, reg, 1, data, 1); +-#elif defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER ++#elif defined CONFIG_AXP221_POWER || defined CONFIG_AXP803_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER + # ifdef CONFIG_MACH_SUN6I + return p2wi_read(reg, data); + # elif defined CONFIG_MACH_SUN8I_R40 +@@ -82,7 +82,7 @@ int pmic_bus_write(u8 reg, u8 data) + return i2c_write(AXP152_I2C_ADDR, reg, 1, &data, 1); + #elif defined CONFIG_AXP209_POWER + return i2c_write(AXP209_I2C_ADDR, reg, 1, &data, 1); +-#elif defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER ++#elif defined CONFIG_AXP221_POWER || defined CONFIG_AXP803_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER + # ifdef CONFIG_MACH_SUN6I + return p2wi_write(reg, data); + # elif defined CONFIG_MACH_SUN8I_R40 +diff --git a/arch/arm/mach-sunxi/rsb.c b/arch/arm/mach-sunxi/rsb.c +index 6fd11f1529..28d05e962a 100644 +--- a/arch/arm/mach-sunxi/rsb.c ++++ b/arch/arm/mach-sunxi/rsb.c +@@ -20,7 +20,7 @@ static int rsb_set_device_mode(void); + + static void rsb_cfg_io(void) + { +-#ifdef CONFIG_MACH_SUN8I ++#if defined CONFIG_MACH_SUN8I || defined CONFIG_MACH_SUN50I + sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL_R_RSB); + sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL_R_RSB); + sunxi_gpio_set_pull(SUNXI_GPL(0), 1); +diff --git a/board/sunxi/board.c b/board/sunxi/board.c +index 70e01437c4..192cf8ca45 100644 +--- a/board/sunxi/board.c ++++ b/board/sunxi/board.c +@@ -519,26 +519,27 @@ void sunxi_board_init(void) + #endif + + #if defined CONFIG_AXP152_POWER || defined CONFIG_AXP209_POWER || \ +- defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \ +- defined CONFIG_AXP818_POWER ++ defined CONFIG_AXP221_POWER || defined CONFIG_AXP803_POWER || \ ++ defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER + power_failed = axp_init(); + +-#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \ +- defined CONFIG_AXP818_POWER ++#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP803_POWER || \ ++ defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER + power_failed |= axp_set_dcdc1(CONFIG_AXP_DCDC1_VOLT); + #endif + power_failed |= axp_set_dcdc2(CONFIG_AXP_DCDC2_VOLT); + power_failed |= axp_set_dcdc3(CONFIG_AXP_DCDC3_VOLT); +-#if !defined(CONFIG_AXP209_POWER) && !defined(CONFIG_AXP818_POWER) ++#if !defined(CONFIG_AXP209_POWER) && !defined(CONFIG_AXP803_POWER) && \ ++ !defined(CONFIG_AXP818_POWER) + power_failed |= axp_set_dcdc4(CONFIG_AXP_DCDC4_VOLT); + #endif +-#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \ +- defined CONFIG_AXP818_POWER ++#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP803_POWER || \ ++ defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER + power_failed |= axp_set_dcdc5(CONFIG_AXP_DCDC5_VOLT); + #endif + +-#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \ +- defined CONFIG_AXP818_POWER ++#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP803_POWER || \ ++ defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER + power_failed |= axp_set_aldo1(CONFIG_AXP_ALDO1_VOLT); + #endif + power_failed |= axp_set_aldo2(CONFIG_AXP_ALDO2_VOLT); +@@ -549,8 +550,8 @@ void sunxi_board_init(void) + power_failed |= axp_set_aldo4(CONFIG_AXP_ALDO4_VOLT); + #endif + +-#if defined(CONFIG_AXP221_POWER) || defined(CONFIG_AXP809_POWER) || \ +- defined(CONFIG_AXP818_POWER) ++#if defined(CONFIG_AXP221_POWER) || defined(CONFIG_AXP803_POWER) || \ ++ defined(CONFIG_AXP809_POWER) || defined(CONFIG_AXP818_POWER) + power_failed |= axp_set_dldo(1, CONFIG_AXP_DLDO1_VOLT); + power_failed |= axp_set_dldo(2, CONFIG_AXP_DLDO2_VOLT); + #if !defined CONFIG_AXP809_POWER +@@ -562,13 +563,17 @@ void sunxi_board_init(void) + power_failed |= axp_set_eldo(3, CONFIG_AXP_ELDO3_VOLT); + #endif + +-#ifdef CONFIG_AXP818_POWER ++#if defined CONFIG_AXP803_POWER || defined CONFIG_AXP818_POWER + power_failed |= axp_set_fldo(1, CONFIG_AXP_FLDO1_VOLT); + power_failed |= axp_set_fldo(2, CONFIG_AXP_FLDO2_VOLT); ++#endif ++ ++#ifdef CONFIG_AXP818_POWER + power_failed |= axp_set_fldo(3, CONFIG_AXP_FLDO3_VOLT); + #endif + +-#if defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER ++#if defined CONFIG_AXP803_POWER || defined CONFIG_AXP809_POWER || \ ++ defined CONFIG_AXP818_POWER + power_failed |= axp_set_sw(IS_ENABLED(CONFIG_AXP_SW_ON)); + #endif + #endif +diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig +index d8c107e206..2da80ae04b 100644 +--- a/drivers/power/Kconfig ++++ b/drivers/power/Kconfig +@@ -11,8 +11,9 @@ choice + depends on ARCH_SUNXI + default AXP209_POWER if MACH_SUN4I || MACH_SUN5I || MACH_SUN7I + default AXP221_POWER if MACH_SUN6I || MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_R40 ++ default AXP803_POWER if MACH_SUN50I + default AXP818_POWER if MACH_SUN8I_A83T +- default SUNXI_NO_PMIC if MACH_SUNXI_H3_H5 || MACH_SUN50I ++ default SUNXI_NO_PMIC if MACH_SUNXI_H3_H5 + + config SUNXI_NO_PMIC + bool "board without a pmic" +@@ -43,6 +44,13 @@ config AXP221_POWER + Select this to enable support for the axp221/axp223 pmic found on most + A23 and A31 boards. + ++config AXP803_POWER ++ bool "axp803 pmic support" ++ depends on MACH_SUN50I ++ select CMD_POWEROFF ++ ---help--- ++ Say y here to enable support for the axp803 pmic found on A64 boards. ++ + config AXP809_POWER + bool "axp809 pmic support" + depends on MACH_SUN9I +@@ -69,25 +77,25 @@ endchoice + + config AXP_DCDC1_VOLT + int "axp pmic dcdc1 voltage" +- depends on AXP221_POWER || AXP809_POWER || AXP818_POWER +- default 3300 if AXP818_POWER || MACH_SUN8I_R40 ++ depends on AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER ++ default 3300 if AXP818_POWER || MACH_SUN8I_R40 || MACH_SUN50I + default 3000 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I + ---help--- + Set the voltage (mV) to program the axp pmic dcdc1 at, set to 0 to + disable dcdc1. On A23 / A31 / A33 (axp221) boards dcdc1 is used for + generic 3.3V IO voltage for external devices like the lcd-panal and + sdcard interfaces, etc. On most boards dcdc1 is undervolted to 3.0V to +- save battery. On A31 devices dcdc1 is also used for VCC-IO. On A83T +- dcdc1 is used for VCC-IO, nand, usb0, sd , etc. On A80 dcdc1 normally +- powers some of the pingroups, NAND/eMMC, SD/MMC, and USB OTG. ++ save battery. On A31 devices dcdc1 is also used for VCC-IO. On A83T and ++ A64 dcdc1 is used for VCC-IO, nand, usb0, sd , etc. On A80 dcdc1 ++ normally powers some of the pingroups, NAND/eMMC, SD/MMC, and USB OTG. + + config AXP_DCDC2_VOLT + int "axp pmic dcdc2 voltage" +- depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER + default 900 if AXP818_POWER + default 1400 if AXP152_POWER || AXP209_POWER + default 1200 if MACH_SUN6I +- default 1100 if MACH_SUN8I ++ default 1100 if MACH_SUN8I || MACH_SUN50I + default 0 if MACH_SUN9I + ---help--- + Set the voltage (mV) to program the axp pmic dcdc2 at, set to 0 to +@@ -98,14 +106,15 @@ config AXP_DCDC2_VOLT + On A80 boards dcdc2 powers the GPU and can be left off. + On A83T boards dcdc2 is used for VDD-CPUA(cluster 0) and should be 0.9V. + On R40 boards dcdc2 is VDD-CPU and should be 1.1V ++ On A64 boards dcdc2 is used with dcdc3 for VDD-CPU and should be 1.1V. + + config AXP_DCDC3_VOLT + int "axp pmic dcdc3 voltage" +- depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER + default 900 if AXP809_POWER || AXP818_POWER + default 1500 if AXP152_POWER + default 1250 if AXP209_POWER +- default 1100 if MACH_SUN8I_R40 ++ default 1100 if MACH_SUN8I_R40 || MACH_SUN50I + default 1200 if MACH_SUN6I || MACH_SUN8I + ---help--- + Set the voltage (mV) to program the axp pmic dcdc3 at, set to 0 to +@@ -117,39 +126,42 @@ config AXP_DCDC3_VOLT + On A80 boards dcdc3 is used for VDD-CPUA(cluster 0) and should be 0.9V. + On A83T boards dcdc3 is used for VDD-CPUB(cluster 1) and should be 0.9V. + On R40 boards dcdc3 is VDD-SYS and VDD-GPU and should be 1.1V. ++ On A64 boards dcdc3 is used with dcdc2 for VDD-CPU and should be 1.1V. + + config AXP_DCDC4_VOLT + int "axp pmic dcdc4 voltage" +- depends on AXP152_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP152_POWER || AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER + default 1250 if AXP152_POWER + default 1200 if MACH_SUN6I +- default 0 if MACH_SUN8I ++ default 0 if MACH_SUN8I || MACH_SUN50I + default 900 if MACH_SUN9I + ---help--- + Set the voltage (mV) to program the axp pmic dcdc4 at, set to 0 to + disable dcdc4. + On A10s boards with an axp152 dcdc4 is VDD-INT-DLL and should be 1.25V. + On A31 boards dcdc4 is used for VDD-SYS and should be 1.2V. +- On A23 / A33 boards dcdc4 is unused and should be disabled. ++ On A23 / A33 / A64 boards dcdc4 is unused and should be disabled. + On A80 boards dcdc4 powers VDD-SYS, HDMI, USB OTG and should be 0.9V. + On A83T boards dcdc4 is used for VDD-GPU. + + config AXP_DCDC5_VOLT + int "axp pmic dcdc5 voltage" +- depends on AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER + default 1500 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I ++ default 1350 if MACH_SUN50I + ---help--- + Set the voltage (mV) to program the axp pmic dcdc5 at, set to 0 to + disable dcdc5. +- On A23 / A31 / A33 / A80 / A83T / R40 boards dcdc5 is VCC-DRAM and ++ On A23 / A31 / A33 / A64 / A80 / A83T / R40 boards dcdc5 is VCC-DRAM and + should be 1.5V, 1.35V if DDR3L is used. + + config AXP_ALDO1_VOLT + int "axp pmic (a)ldo1 voltage" +- depends on AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER + default 0 if MACH_SUN6I || MACH_SUN8I_R40 + default 1800 if MACH_SUN8I_A83T + default 3000 if MACH_SUN8I || MACH_SUN9I ++ default 2800 if MACH_SUN50I + ---help--- + Set the voltage (mV) to program the axp pmic aldo1 at, set to 0 to + disable aldo1. +@@ -158,14 +170,16 @@ config AXP_ALDO1_VOLT + On A80 boards aldo1 powers the USB hosts and should be 3.0V. + On A83T / H8 boards aldo1 is used for MIPI CSI, DSI, HDMI, EFUSE, and + should be 1.8V. ++ On A64 boards aldo1 powers PE pingroup and CSI and should be 2.8V. + + config AXP_ALDO2_VOLT + int "axp pmic (a)ldo2 voltage" +- depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER + default 3000 if AXP152_POWER || AXP209_POWER + default 0 if MACH_SUN6I || MACH_SUN9I + default 1800 if MACH_SUN8I_A83T + default 2500 if MACH_SUN8I ++ default 1800 if MACH_SUN50I + ---help--- + Set the voltage (mV) to program the axp pmic aldo2 at, set to 0 to + disable aldo2. +@@ -176,17 +190,18 @@ config AXP_ALDO2_VOLT + On A80 boards aldo2 powers PB pingroup and camera IO and can be left off. + On A83T / H8 boards aldo2 powers VDD-DLL, VCC18-PLL, CPVDD, VDD18-ADC, + LPDDR2, and the codec. It should be 1.8V. ++ On A64 boards aldo2 powers PL pingroup and should be 1.8V. + + config AXP_ALDO3_VOLT + int "axp pmic (a)ldo3 voltage" +- depends on AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP209_POWER || AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER + default 0 if AXP209_POWER || MACH_SUN9I +- default 3000 if MACH_SUN6I || MACH_SUN8I ++ default 3000 if MACH_SUN6I || MACH_SUN8I || MACH_SUN50I + ---help--- + Set the voltage (mV) to program the axp pmic aldo3 at, set to 0 to + disable aldo3. + On A10(s) / A13 / A20 boards aldo3 should be 2.8V. +- On A23 / A31 / A33 / R40 boards aldo3 is VCC-PLL and AVCC and should ++ On A23 / A31 / A33 / A64 / R40 boards aldo3 is VCC-PLL and AVCC and should + be 3.0V. + On A80 boards aldo3 is normally not used. + On A83T / H8 boards aldo3 is AVCC, VCC-PL, and VCC-LED, and should be +@@ -203,17 +218,19 @@ config AXP_ALDO4_VOLT + + config AXP_DLDO1_VOLT + int "axp pmic dldo1 voltage" +- depends on AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER ++ default 3300 if MACH_SUN50I + default 0 + ---help--- + Set the voltage (mV) to program the axp pmic dldo1 at, set to 0 to + disable dldo1. On sun6i (A31) boards with ethernet dldo1 is often used + to power the ethernet phy. On A23, A33 and A80 boards this is often +- used to power the wifi. ++ used to power the wifi. On A64 boards this is often used to power the ++ HDMI. + + config AXP_DLDO2_VOLT + int "axp pmic dldo2 voltage" +- depends on AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER + default 3000 if MACH_SUN9I + default 0 + ---help--- +@@ -223,7 +240,7 @@ config AXP_DLDO2_VOLT + + config AXP_DLDO3_VOLT + int "axp pmic dldo3 voltage" +- depends on AXP221_POWER || AXP818_POWER ++ depends on AXP221_POWER || AXP803_POWER || AXP818_POWER + default 0 + ---help--- + Set the voltage (mV) to program the axp pmic dldo3 at, set to 0 to +@@ -231,7 +248,7 @@ config AXP_DLDO3_VOLT + + config AXP_DLDO4_VOLT + int "axp pmic dldo4 voltage" +- depends on AXP221_POWER || AXP818_POWER ++ depends on AXP221_POWER || AXP803_POWER || AXP818_POWER + default 0 + ---help--- + Set the voltage (mV) to program the axp pmic dldo4 at, set to 0 to +@@ -239,15 +256,17 @@ config AXP_DLDO4_VOLT + + config AXP_ELDO1_VOLT + int "axp pmic eldo1 voltage" +- depends on AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER ++ default 1800 if MACH_SUN50I + default 0 + ---help--- + Set the voltage (mV) to program the axp pmic eldo1 at, set to 0 to + disable eldo1. ++ On A64 boards it's used for the codec and should be 1.8V. + + config AXP_ELDO2_VOLT + int "axp pmic eldo2 voltage" +- depends on AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER + default 0 + ---help--- + Set the voltage (mV) to program the axp pmic eldo2 at, set to 0 to +@@ -255,7 +274,7 @@ config AXP_ELDO2_VOLT + + config AXP_ELDO3_VOLT + int "axp pmic eldo3 voltage" +- depends on AXP221_POWER || AXP809_POWER || AXP818_POWER ++ depends on AXP221_POWER || AXP803_POWER || AXP809_POWER || AXP818_POWER + default 3000 if MACH_SUN9I + default 0 + ---help--- +@@ -267,8 +286,8 @@ config AXP_ELDO3_VOLT + + config AXP_FLDO1_VOLT + int "axp pmic fldo1 voltage" +- depends on AXP818_POWER +- default 0 if MACH_SUN8I_A83T ++ depends on AXP803_POWER || AXP818_POWER ++ default 0 + ---help--- + Set the voltage (mV) to program the axp pmic fldo1 at, set to 0 to + disable fldo1. +@@ -277,11 +296,13 @@ config AXP_FLDO1_VOLT + + config AXP_FLDO2_VOLT + int "axp pmic fldo2 voltage" +- depends on AXP818_POWER ++ depends on AXP803_POWER || AXP818_POWER ++ default 1100 if MACH_SUN50I + default 900 if MACH_SUN8I_A83T + ---help--- + Set the voltage (mV) to program the axp pmic fldo2 at, set to 0 to + disable fldo2. ++ On A64 boards fldo2 is VCC-CPUS and should be 1.1V. + On A83T / H8 boards fldo2 is VCC-CPUS and should be 0.9V. + + config AXP_FLDO3_VOLT +@@ -294,7 +315,7 @@ config AXP_FLDO3_VOLT + + config AXP_SW_ON + bool "axp pmic sw on" +- depends on AXP809_POWER || AXP818_POWER ++ depends on AXP803_POWER || AXP809_POWER || AXP818_POWER + default n + ---help--- + Enable to turn on axp pmic sw. +diff --git a/drivers/power/Makefile b/drivers/power/Makefile +index 90a3b00a7c..55413a66d2 100644 +--- a/drivers/power/Makefile ++++ b/drivers/power/Makefile +@@ -8,6 +8,7 @@ + obj-$(CONFIG_AXP152_POWER) += axp152.o + obj-$(CONFIG_AXP209_POWER) += axp209.o + obj-$(CONFIG_AXP221_POWER) += axp221.o ++obj-$(CONFIG_AXP803_POWER) += axp803.o + obj-$(CONFIG_AXP809_POWER) += axp809.o + obj-$(CONFIG_AXP818_POWER) += axp818.o + obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o +diff --git a/drivers/power/axp803.c b/drivers/power/axp803.c +new file mode 100644 +index 0000000000..af20fce8b1 +--- /dev/null ++++ b/drivers/power/axp803.c +@@ -0,0 +1,259 @@ ++/* ++ * AXP803 driver based on AXP818 driver ++ * ++ * Based on axp818.c ++ * (C) Copyright 2015 Vishnu Patekar ++ * ++ * Based on axp221.c ++ * (C) Copyright 2014 Hans de Goede ++ * (C) Copyright 2013 Oliver Schinagl ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static u8 axp803_mvolt_to_cfg(int mvolt, int min, int max, int div) ++{ ++ if (mvolt < min) ++ mvolt = min; ++ else if (mvolt > max) ++ mvolt = max; ++ ++ return (mvolt - min) / div; ++} ++ ++int axp_set_dcdc1(unsigned int mvolt) ++{ ++ int ret; ++ u8 cfg = axp803_mvolt_to_cfg(mvolt, 1600, 3400, 100); ++ ++ if (mvolt == 0) ++ return pmic_bus_clrbits(AXP803_OUTPUT_CTRL1, ++ AXP803_OUTPUT_CTRL1_DCDC1_EN); ++ ++ ret = pmic_bus_write(AXP803_DCDC1_CTRL, cfg); ++ if (ret) ++ return ret; ++ ++ return pmic_bus_setbits(AXP803_OUTPUT_CTRL1, ++ AXP803_OUTPUT_CTRL1_DCDC1_EN); ++} ++ ++int axp_set_dcdc2(unsigned int mvolt) ++{ ++ int ret; ++ u8 cfg; ++ ++ if (mvolt >= 1220) ++ cfg = 70 + axp803_mvolt_to_cfg(mvolt, 1220, 1300, 20); ++ else ++ cfg = axp803_mvolt_to_cfg(mvolt, 500, 1200, 10); ++ ++ if (mvolt == 0) ++ return pmic_bus_clrbits(AXP803_OUTPUT_CTRL1, ++ AXP803_OUTPUT_CTRL1_DCDC2_EN); ++ ++ ret = pmic_bus_write(AXP803_DCDC2_CTRL, cfg); ++ if (ret) ++ return ret; ++ ++ return pmic_bus_setbits(AXP803_OUTPUT_CTRL1, ++ AXP803_OUTPUT_CTRL1_DCDC2_EN); ++} ++ ++int axp_set_dcdc3(unsigned int mvolt) ++{ ++ int ret; ++ u8 cfg; ++ ++ if (mvolt >= 1220) ++ cfg = 70 + axp803_mvolt_to_cfg(mvolt, 1220, 1300, 20); ++ else ++ cfg = axp803_mvolt_to_cfg(mvolt, 500, 1200, 10); ++ ++ if (mvolt == 0) ++ return pmic_bus_clrbits(AXP803_OUTPUT_CTRL1, ++ AXP803_OUTPUT_CTRL1_DCDC3_EN); ++ ++ ret = pmic_bus_write(AXP803_DCDC3_CTRL, cfg); ++ if (ret) ++ return ret; ++ ++ return pmic_bus_setbits(AXP803_OUTPUT_CTRL1, ++ AXP803_OUTPUT_CTRL1_DCDC3_EN); ++} ++ ++int axp_set_dcdc5(unsigned int mvolt) ++{ ++ int ret; ++ u8 cfg; ++ ++ if (mvolt >= 1140) ++ cfg = 32 + axp803_mvolt_to_cfg(mvolt, 1140, 1840, 20); ++ else ++ cfg = axp803_mvolt_to_cfg(mvolt, 800, 1120, 10); ++ ++ if (mvolt == 0) ++ return pmic_bus_clrbits(AXP803_OUTPUT_CTRL1, ++ AXP803_OUTPUT_CTRL1_DCDC5_EN); ++ ++ ret = pmic_bus_write(AXP803_DCDC5_CTRL, cfg); ++ if (ret) ++ return ret; ++ ++ return pmic_bus_setbits(AXP803_OUTPUT_CTRL1, ++ AXP803_OUTPUT_CTRL1_DCDC5_EN); ++} ++ ++int axp_set_aldo(int aldo_num, unsigned int mvolt) ++{ ++ int ret; ++ u8 cfg; ++ ++ if (aldo_num < 1 || aldo_num > 3) ++ return -EINVAL; ++ ++ if (mvolt == 0) ++ return pmic_bus_clrbits(AXP803_OUTPUT_CTRL3, ++ AXP803_OUTPUT_CTRL3_ALDO1_EN << (aldo_num - 1)); ++ ++ cfg = axp803_mvolt_to_cfg(mvolt, 700, 3300, 100); ++ ret = pmic_bus_write(AXP803_ALDO1_CTRL + (aldo_num - 1), cfg); ++ if (ret) ++ return ret; ++ ++ return pmic_bus_setbits(AXP803_OUTPUT_CTRL3, ++ AXP803_OUTPUT_CTRL3_ALDO1_EN << (aldo_num - 1)); ++} ++ ++/* TODO: re-work other AXP drivers to consolidate ALDO functions. */ ++int axp_set_aldo1(unsigned int mvolt) ++{ ++ return axp_set_aldo(1, mvolt); ++} ++ ++int axp_set_aldo2(unsigned int mvolt) ++{ ++ return axp_set_aldo(2, mvolt); ++} ++ ++int axp_set_aldo3(unsigned int mvolt) ++{ ++ return axp_set_aldo(3, mvolt); ++} ++ ++int axp_set_dldo(int dldo_num, unsigned int mvolt) ++{ ++ int ret; ++ u8 cfg; ++ ++ if (dldo_num < 1 || dldo_num > 4) ++ return -EINVAL; ++ ++ if (mvolt == 0) ++ return pmic_bus_clrbits(AXP803_OUTPUT_CTRL2, ++ AXP803_OUTPUT_CTRL2_DLDO1_EN << (dldo_num - 1)); ++ ++ cfg = axp803_mvolt_to_cfg(mvolt, 700, 3300, 100); ++ if (dldo_num == 2 && mvolt > 3300) ++ cfg += 1 + axp803_mvolt_to_cfg(mvolt, 3400, 4200, 200); ++ ret = pmic_bus_write(AXP803_DLDO1_CTRL + (dldo_num - 1), cfg); ++ if (ret) ++ return ret; ++ ++ return pmic_bus_setbits(AXP803_OUTPUT_CTRL2, ++ AXP803_OUTPUT_CTRL2_DLDO1_EN << (dldo_num - 1)); ++} ++ ++int axp_set_eldo(int eldo_num, unsigned int mvolt) ++{ ++ int ret; ++ u8 cfg; ++ ++ if (eldo_num < 1 || eldo_num > 3) ++ return -EINVAL; ++ ++ if (mvolt == 0) ++ return pmic_bus_clrbits(AXP803_OUTPUT_CTRL2, ++ AXP803_OUTPUT_CTRL2_ELDO1_EN << (eldo_num - 1)); ++ ++ cfg = axp803_mvolt_to_cfg(mvolt, 700, 1900, 50); ++ ret = pmic_bus_write(AXP803_ELDO1_CTRL + (eldo_num - 1), cfg); ++ if (ret) ++ return ret; ++ ++ return pmic_bus_setbits(AXP803_OUTPUT_CTRL2, ++ AXP803_OUTPUT_CTRL2_ELDO1_EN << (eldo_num - 1)); ++} ++ ++int axp_set_fldo(int fldo_num, unsigned int mvolt) ++{ ++ int ret; ++ u8 cfg; ++ ++ if (fldo_num < 1 || fldo_num > 2) ++ return -EINVAL; ++ ++ if (mvolt == 0) ++ return pmic_bus_clrbits(AXP803_OUTPUT_CTRL3, ++ AXP803_OUTPUT_CTRL3_FLDO1_EN << (fldo_num - 1)); ++ ++ cfg = axp803_mvolt_to_cfg(mvolt, 700, 1450, 50); ++ ret = pmic_bus_write(AXP803_FLDO1_CTRL + (fldo_num - 1), cfg); ++ if (ret) ++ return ret; ++ ++ return pmic_bus_setbits(AXP803_OUTPUT_CTRL3, ++ AXP803_OUTPUT_CTRL3_FLDO1_EN << (fldo_num - 1)); ++} ++ ++int axp_set_sw(bool on) ++{ ++ if (on) ++ return pmic_bus_setbits(AXP803_OUTPUT_CTRL2, ++ AXP803_OUTPUT_CTRL2_SW_EN); ++ ++ return pmic_bus_clrbits(AXP803_OUTPUT_CTRL2, ++ AXP803_OUTPUT_CTRL2_SW_EN); ++} ++ ++int axp_init(void) ++{ ++ u8 axp_chip_id; ++ int ret; ++ ++ ret = pmic_bus_init(); ++ if (ret) ++ return ret; ++ ++ ret = pmic_bus_read(AXP803_CHIP_ID, &axp_chip_id); ++ if (ret) ++ return ret; ++ ++ if (!(axp_chip_id == 0x51)) ++ return -ENODEV; ++ else ++ return ret; ++ ++ return 0; ++} ++ ++/* ARM64 has its own poweroff implementation using PSCI */ ++#ifndef CONFIG_ARM64 ++int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ pmic_bus_write(AXP803_SHUTDOWN, AXP803_SHUTDOWN_POWEROFF); ++ ++ /* infinite loop during shutdown */ ++ while (1) ++ ; ++ ++ /* not reached */ ++ return 0; ++} ++#endif +diff --git a/drivers/power/axp818.c b/drivers/power/axp818.c +index af4d7a6903..ad0c330ca5 100644 +--- a/drivers/power/axp818.c ++++ b/drivers/power/axp818.c +@@ -162,7 +162,7 @@ int axp_set_dldo(int dldo_num, unsigned int mvolt) + cfg = axp818_mvolt_to_cfg(mvolt, 700, 3300, 100); + if (dldo_num == 2 && mvolt > 3300) + cfg += 1 + axp818_mvolt_to_cfg(mvolt, 3400, 4200, 200); +- ret = pmic_bus_write(AXP818_ELDO1_CTRL + (dldo_num - 1), cfg); ++ ret = pmic_bus_write(AXP818_DLDO1_CTRL + (dldo_num - 1), cfg); + if (ret) + return ret; + +diff --git a/include/axp803.h b/include/axp803.h +new file mode 100644 +index 0000000000..b382f3a5ec +--- /dev/null ++++ b/include/axp803.h +@@ -0,0 +1,73 @@ ++/* ++ * (C) Copyright 2016 Icenowy Zheng ++ * ++ * Based on axp818.h, which is: ++ * (C) Copyright 2015 Vishnu Patekar ++ * ++ * X-Powers AXP803 Power Management IC driver ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#define AXP803_CHIP_ID 0x03 ++ ++#define AXP803_OUTPUT_CTRL1 0x10 ++#define AXP803_OUTPUT_CTRL1_DCDC1_EN (1 << 0) ++#define AXP803_OUTPUT_CTRL1_DCDC2_EN (1 << 1) ++#define AXP803_OUTPUT_CTRL1_DCDC3_EN (1 << 2) ++#define AXP803_OUTPUT_CTRL1_DCDC4_EN (1 << 3) ++#define AXP803_OUTPUT_CTRL1_DCDC5_EN (1 << 4) ++#define AXP803_OUTPUT_CTRL1_DCDC6_EN (1 << 5) ++#define AXP803_OUTPUT_CTRL2 0x12 ++#define AXP803_OUTPUT_CTRL2_ELDO1_EN (1 << 0) ++#define AXP803_OUTPUT_CTRL2_ELDO2_EN (1 << 1) ++#define AXP803_OUTPUT_CTRL2_ELDO3_EN (1 << 2) ++#define AXP803_OUTPUT_CTRL2_DLDO1_EN (1 << 3) ++#define AXP803_OUTPUT_CTRL2_DLDO2_EN (1 << 4) ++#define AXP803_OUTPUT_CTRL2_DLDO3_EN (1 << 5) ++#define AXP803_OUTPUT_CTRL2_DLDO4_EN (1 << 6) ++#define AXP803_OUTPUT_CTRL2_SW_EN (1 << 7) ++#define AXP803_OUTPUT_CTRL3 0x13 ++#define AXP803_OUTPUT_CTRL3_FLDO1_EN (1 << 2) ++#define AXP803_OUTPUT_CTRL3_FLDO2_EN (1 << 3) ++#define AXP803_OUTPUT_CTRL3_ALDO1_EN (1 << 5) ++#define AXP803_OUTPUT_CTRL3_ALDO2_EN (1 << 6) ++#define AXP803_OUTPUT_CTRL3_ALDO3_EN (1 << 7) ++ ++#define AXP803_DLDO1_CTRL 0x15 ++#define AXP803_DLDO2_CTRL 0x16 ++#define AXP803_DLDO3_CTRL 0x17 ++#define AXP803_DLDO4_CTRL 0x18 ++#define AXP803_ELDO1_CTRL 0x19 ++#define AXP803_ELDO2_CTRL 0x1a ++#define AXP803_ELDO3_CTRL 0x1b ++#define AXP803_FLDO1_CTRL 0x1c ++#define AXP803_FLDO2_CTRL 0x1d ++#define AXP803_DCDC1_CTRL 0x20 ++#define AXP803_DCDC2_CTRL 0x21 ++#define AXP803_DCDC3_CTRL 0x22 ++#define AXP803_DCDC4_CTRL 0x23 ++#define AXP803_DCDC5_CTRL 0x24 ++#define AXP803_DCDC6_CTRL 0x25 ++ ++#define AXP803_ALDO1_CTRL 0x28 ++#define AXP803_ALDO2_CTRL 0x29 ++#define AXP803_ALDO3_CTRL 0x2a ++ ++#define AXP803_SHUTDOWN 0x32 ++#define AXP803_SHUTDOWN_POWEROFF (1 << 7) ++ ++/* For axp_gpio.c */ ++#define AXP_POWER_STATUS 0x00 ++#define AXP_POWER_STATUS_VBUS_PRESENT (1 << 5) ++#define AXP_VBUS_IPSOUT 0x30 ++#define AXP_VBUS_IPSOUT_DRIVEBUS (1 << 2) ++#define AXP_MISC_CTRL 0x8f ++#define AXP_MISC_CTRL_N_VBUSEN_FUNC (1 << 4) ++#define AXP_GPIO0_CTRL 0x90 ++#define AXP_GPIO1_CTRL 0x92 ++#define AXP_GPIO_CTRL_OUTPUT_LOW 0x00 /* Drive pin low */ ++#define AXP_GPIO_CTRL_OUTPUT_HIGH 0x01 /* Drive pin high */ ++#define AXP_GPIO_CTRL_INPUT 0x02 /* Input */ ++#define AXP_GPIO_STATE 0x94 ++#define AXP_GPIO_STATE_OFFSET 0 +diff --git a/include/axp_pmic.h b/include/axp_pmic.h +index d789ad8086..8cb4d5763c 100644 +--- a/include/axp_pmic.h ++++ b/include/axp_pmic.h +@@ -16,6 +16,9 @@ + #ifdef CONFIG_AXP221_POWER + #include + #endif ++#ifdef CONFIG_AXP803_POWER ++#include ++#endif + #ifdef CONFIG_AXP809_POWER + #include + #endif diff --git a/patch/u-boot/u-boot-sunxi/03-vidconsole-fix.patch b/patch/u-boot/u-boot-sunxi/03-vidconsole-fix.patch new file mode 100644 index 000000000..847958401 --- /dev/null +++ b/patch/u-boot/u-boot-sunxi/03-vidconsole-fix.patch @@ -0,0 +1,25 @@ +From e4421d697d889d1a802babfbd1d0839bc92884a8 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Thu, 31 Aug 2017 21:53:07 -0700 +Subject: [PATCH] vidconsole: flush dcache on putc + +Otherwise characters that are being typed are not displayed until +user presses "enter". + +Signed-off-by: Vasily Khoruzhick +--- + drivers/video/vidconsole-uclass.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c +index b5afd72227..44c716c665 100644 +--- a/drivers/video/vidconsole-uclass.c ++++ b/drivers/video/vidconsole-uclass.c +@@ -163,6 +163,7 @@ static void vidconsole_putc(struct stdio_dev *sdev, const char ch) + struct udevice *dev = sdev->priv; + + vidconsole_put_char(dev, ch); ++ video_sync(dev->parent); + } + + static void vidconsole_puts(struct stdio_dev *sdev, const char *s) diff --git a/patch/u-boot/u-boot-sunxi/04-pwm_backlight-regulator-optional.patch b/patch/u-boot/u-boot-sunxi/04-pwm_backlight-regulator-optional.patch new file mode 100644 index 000000000..57579e0fa --- /dev/null +++ b/patch/u-boot/u-boot-sunxi/04-pwm_backlight-regulator-optional.patch @@ -0,0 +1,62 @@ +From 732153bd114162e8afa22a025d065c5c1d5359f5 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Sun, 17 Sep 2017 09:21:33 -0700 +Subject: [PATCH] video: pwm_backlight: make regulator optional + +u-boot doesn't have dummy regulators, so pwm_backlight probe +will fail if regulator is missing. Make it optional to get this +driver working on platforms where there's no backlight regultor. + +Signed-off-by: Vasily Khoruzhick +--- + drivers/video/pwm_backlight.c | 28 ++++++++++++++++------------ + 1 file changed, 16 insertions(+), 12 deletions(-) + +diff --git a/drivers/video/pwm_backlight.c b/drivers/video/pwm_backlight.c +index fbd7bf7838..05e56ffead 100644 +--- a/drivers/video/pwm_backlight.c ++++ b/drivers/video/pwm_backlight.c +@@ -32,16 +32,21 @@ static int pwm_backlight_enable(struct udevice *dev) + uint duty_cycle; + int ret; + +- plat = dev_get_uclass_platdata(priv->reg); +- debug("%s: Enable '%s', regulator '%s'/'%s'\n", __func__, dev->name, +- priv->reg->name, plat->name); +- ret = regulator_set_enable(priv->reg, true); +- if (ret) { +- debug("%s: Cannot enable regulator for PWM '%s'\n", __func__, +- dev->name); +- return ret; ++ if (priv->reg) { ++ plat = dev_get_uclass_platdata(priv->reg); ++ debug("%s: Enable '%s', regulator '%s'/'%s'\n", __func__, dev->name, ++ priv->reg->name, plat->name); ++ ret = regulator_set_enable(priv->reg, true); ++ if (ret) { ++ debug("%s: Cannot enable regulator for PWM '%s'\n", __func__, ++ dev->name); ++ return ret; ++ } ++ mdelay(120); + } +- mdelay(120); ++ ++ debug("%s: default: %d, min: %d, max: %d\n", __func__, ++ priv->default_level, priv->min_level, priv->max_level); + + duty_cycle = priv->period_ns * (priv->default_level - priv->min_level) / + (priv->max_level - priv->min_level + 1); +@@ -68,10 +73,9 @@ static int pwm_backlight_ofdata_to_platdata(struct udevice *dev) + debug("%s: start\n", __func__); + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "power-supply", &priv->reg); +- if (ret) { ++ if (ret) + debug("%s: Cannot get power supply: ret=%d\n", __func__, ret); +- return ret; +- } ++ + ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable, + GPIOD_IS_OUT); + if (ret) { diff --git a/patch/u-boot/u-boot-sunxi/05-add-pwm-node-DT.patch b/patch/u-boot/u-boot-sunxi/05-add-pwm-node-DT.patch new file mode 100644 index 000000000..2ea03c926 --- /dev/null +++ b/patch/u-boot/u-boot-sunxi/05-add-pwm-node-DT.patch @@ -0,0 +1,31 @@ +From 4644c442c8b14585881995f1c901782c3dad0e46 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Sun, 17 Sep 2017 09:24:13 -0700 +Subject: [PATCH] dts: sunxi: add PWM node for sun50i + +Add PWM definition to sun50i-a64.dtsi - it's compatible with PWM found on H3 + +Signed-off-by: Vasily Khoruzhick +--- + arch/arm/dts/sun50i-a64.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/dts/sun50i-a64.dtsi b/arch/arm/dts/sun50i-a64.dtsi +index 65a344d9ce..00132855ff 100644 +--- a/arch/arm/dts/sun50i-a64.dtsi ++++ b/arch/arm/dts/sun50i-a64.dtsi +@@ -319,6 +319,14 @@ + }; + }; + ++ pwm: pwm@01c21400 { ++ compatible = "allwinner,sun8i-h3-pwm"; ++ reg = <0x01c21400 0x8>; ++ clocks = <&osc24M>; ++ #pwm-cells = <3>; ++ status = "disabled"; ++ }; ++ + uart0: serial@1c28000 { + compatible = "snps,dw-apb-uart"; + reg = <0x01c28000 0x400>; diff --git a/patch/u-boot/u-boot-sunxi/06-bridge-read-edid.patch b/patch/u-boot/u-boot-sunxi/06-bridge-read-edid.patch new file mode 100644 index 000000000..04c205cb4 --- /dev/null +++ b/patch/u-boot/u-boot-sunxi/06-bridge-read-edid.patch @@ -0,0 +1,78 @@ +From e7ce02a71b32cb1845a84dc54b8d596a0c5b04b5 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Fri, 15 Sep 2017 21:14:41 -0700 +Subject: [PATCH] dm: video: bridge: add operation to read EDID + +Add an operation to read EDID, since bridge may have ability to read +EDID from the panel that is connected to it, for example LCD<->eDP bridge. + +Signed-off-by: Vasily Khoruzhick +--- + drivers/video/bridge/video-bridge-uclass.c | 10 ++++++++++ + include/video_bridge.h | 20 ++++++++++++++++++++ + 2 files changed, 30 insertions(+) + +diff --git a/drivers/video/bridge/video-bridge-uclass.c b/drivers/video/bridge/video-bridge-uclass.c +index 07270bac9e..79facd02a6 100644 +--- a/drivers/video/bridge/video-bridge-uclass.c ++++ b/drivers/video/bridge/video-bridge-uclass.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + + int video_bridge_set_backlight(struct udevice *dev, int percent) +@@ -45,6 +46,15 @@ int video_bridge_check_attached(struct udevice *dev) + return ops->check_attached(dev); + } + ++int video_bridge_read_edid(struct udevice *dev, u8 *buf, int buf_size) ++{ ++ struct video_bridge_ops *ops = video_bridge_get_ops(dev); ++ ++ if (!ops || !ops->read_edid) ++ return -ENOSYS; ++ return ops->read_edid(dev, buf, buf_size); ++} ++ + static int video_bridge_pre_probe(struct udevice *dev) + { + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); +diff --git a/include/video_bridge.h b/include/video_bridge.h +index c7b8681849..0699a8dda8 100644 +--- a/include/video_bridge.h ++++ b/include/video_bridge.h +@@ -53,6 +53,16 @@ struct video_bridge_ops { + * @return 0 if OK, -ve on error + */ + int (*set_backlight)(struct udevice *dev, int percent); ++ ++ /** ++ * read_edid() - Read information from EDID ++ * ++ * @dev: Device to read from ++ * @buf: Buffer to read into ++ * @buf_size: Buffer size ++ * @return number of bytes read, <=0 for error ++ */ ++ int (*read_edid)(struct udevice *dev, u8 *buf, int buf_size); + }; + + #define video_bridge_get_ops(dev) \ +@@ -89,4 +99,14 @@ int video_bridge_set_active(struct udevice *dev, bool active); + */ + int video_bridge_check_attached(struct udevice *dev); + ++/** ++ * video_bridge_read_edid() - Read information from EDID ++ * ++ * @dev: Device to read from ++ * @buf: Buffer to read into ++ * @buf_size: Buffer size ++ * @return number of bytes read, <=0 for error ++ */ ++int video_bridge_read_edid(struct udevice *dev, u8 *buf, int buf_size); ++ + #endif diff --git a/patch/u-boot/u-boot-sunxi/07-split-anx9804.patch b/patch/u-boot/u-boot-sunxi/07-split-anx9804.patch new file mode 100644 index 000000000..0c199e1b7 --- /dev/null +++ b/patch/u-boot/u-boot-sunxi/07-split-anx9804.patch @@ -0,0 +1,186 @@ +From 0b082a8e4e751a2b95ebf450945c055f2cf3d4e2 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Fri, 15 Sep 2017 23:35:40 -0700 +Subject: [PATCH] video: anx9804: split out registers definitions into a + separate header + +This header will be used in anx6345 driver + +Signed-off-by: Vasily Khoruzhick +--- + drivers/video/anx9804.c | 54 +-------------------------- + include/anx98xx-edp.h | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 99 insertions(+), 53 deletions(-) + create mode 100644 include/anx98xx-edp.h + +diff --git a/drivers/video/anx9804.c b/drivers/video/anx9804.c +index 37ad69a039..67f7da7d18 100755 +--- a/drivers/video/anx9804.c ++++ b/drivers/video/anx9804.c +@@ -12,61 +12,9 @@ + + #include + #include ++#include + #include "anx9804.h" + +-/* Registers at i2c address 0x38 */ +- +-#define ANX9804_HDCP_CONTROL_0_REG 0x01 +- +-#define ANX9804_SYS_CTRL2_REG 0x81 +-#define ANX9804_SYS_CTRL2_CHA_STA 0x04 +- +-#define ANX9804_SYS_CTRL3_REG 0x82 +-#define ANX9804_SYS_CTRL3_VALID_CTRL BIT(0) +-#define ANX9804_SYS_CTRL3_F_VALID BIT(1) +-#define ANX9804_SYS_CTRL3_HPD_CTRL BIT(4) +-#define ANX9804_SYS_CTRL3_F_HPD BIT(5) +- +-#define ANX9804_LINK_BW_SET_REG 0xa0 +-#define ANX9804_LANE_COUNT_SET_REG 0xa1 +-#define ANX9804_TRAINING_PTN_SET_REG 0xa2 +-#define ANX9804_TRAINING_LANE0_SET_REG 0xa3 +-#define ANX9804_TRAINING_LANE1_SET_REG 0xa4 +-#define ANX9804_TRAINING_LANE2_SET_REG 0xa5 +-#define ANX9804_TRAINING_LANE3_SET_REG 0xa6 +- +-#define ANX9804_LINK_TRAINING_CTRL_REG 0xa8 +-#define ANX9804_LINK_TRAINING_CTRL_EN BIT(0) +- +-#define ANX9804_LINK_DEBUG_REG 0xb8 +-#define ANX9804_PLL_CTRL_REG 0xc7 +-#define ANX9804_ANALOG_POWER_DOWN_REG 0xc8 +- +-/* Registers at i2c address 0x39 */ +- +-#define ANX9804_DEV_IDH_REG 0x03 +- +-#define ANX9804_POWERD_CTRL_REG 0x05 +-#define ANX9804_POWERD_AUDIO BIT(4) +- +-#define ANX9804_RST_CTRL_REG 0x06 +- +-#define ANX9804_RST_CTRL2_REG 0x07 +-#define ANX9804_RST_CTRL2_AUX BIT(2) +-#define ANX9804_RST_CTRL2_AC_MODE BIT(6) +- +-#define ANX9804_VID_CTRL1_REG 0x08 +-#define ANX9804_VID_CTRL1_VID_EN BIT(7) +-#define ANX9804_VID_CTRL1_EDGE BIT(0) +- +-#define ANX9804_VID_CTRL2_REG 0x09 +-#define ANX9804_ANALOG_DEBUG_REG1 0xdc +-#define ANX9804_ANALOG_DEBUG_REG3 0xde +-#define ANX9804_PLL_FILTER_CTRL1 0xdf +-#define ANX9804_PLL_FILTER_CTRL3 0xe1 +-#define ANX9804_PLL_FILTER_CTRL 0xe2 +-#define ANX9804_PLL_CTRL3 0xe6 +- + /** + * anx9804_init() - Init anx9804 parallel lcd to edp bridge chip + * +diff --git a/include/anx98xx-edp.h b/include/anx98xx-edp.h +new file mode 100644 +index 0000000000..f7e8baa167 +--- /dev/null ++++ b/include/anx98xx-edp.h +@@ -0,0 +1,98 @@ ++/* ++ * Copyright (C) 2015 Hans de Goede ++ * Copyright (C) 2017 Vasily Khoruzhick ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++/* Registers at i2c address 0x38 */ ++ ++#define ANX9804_HDCP_CONTROL_0_REG 0x01 ++ ++#define ANX9804_SYS_CTRL1_REG 0x80 ++#define ANX9804_SYS_CTRL1_PD_IO 0x80 ++#define ANX9804_SYS_CTRL1_PD_VID 0x40 ++#define ANX9804_SYS_CTRL1_PD_LINK 0x20 ++#define ANX9804_SYS_CTRL1_PD_TOTAL 0x10 ++#define ANX9804_SYS_CTRL1_MODE_SEL 0x08 ++#define ANX9804_SYS_CTRL1_DET_STA 0x04 ++#define ANX9804_SYS_CTRL1_FORCE_DET 0x02 ++#define ANX9804_SYS_CTRL1_DET_CTRL 0x01 ++ ++#define ANX9804_SYS_CTRL2_REG 0x81 ++#define ANX9804_SYS_CTRL2_CHA_STA 0x04 ++ ++#define ANX9804_SYS_CTRL3_REG 0x82 ++#define ANX9804_SYS_CTRL3_VALID_CTRL BIT(0) ++#define ANX9804_SYS_CTRL3_F_VALID BIT(1) ++#define ANX9804_SYS_CTRL3_HPD_CTRL BIT(4) ++#define ANX9804_SYS_CTRL3_F_HPD BIT(5) ++ ++#define ANX9804_LINK_BW_SET_REG 0xa0 ++#define ANX9804_LANE_COUNT_SET_REG 0xa1 ++#define ANX9804_TRAINING_PTN_SET_REG 0xa2 ++#define ANX9804_TRAINING_LANE0_SET_REG 0xa3 ++#define ANX9804_TRAINING_LANE1_SET_REG 0xa4 ++#define ANX9804_TRAINING_LANE2_SET_REG 0xa5 ++#define ANX9804_TRAINING_LANE3_SET_REG 0xa6 ++ ++#define ANX9804_LINK_TRAINING_CTRL_REG 0xa8 ++#define ANX9804_LINK_TRAINING_CTRL_EN BIT(0) ++ ++#define ANX9804_LINK_DEBUG_REG 0xb8 ++#define ANX9804_PLL_CTRL_REG 0xc7 ++#define ANX9804_ANALOG_POWER_DOWN_REG 0xc8 ++ ++#define ANX9804_AUX_CH_STA 0xe0 ++#define ANX9804_AUX_BUSY BIT(4) ++#define ANX9804_AUX_STATUS_MASK 0x0f ++ ++#define ANX9804_DP_AUX_RX_COMM 0xe3 ++#define ANX9804_AUX_RX_COMM_I2C_DEFER BIT(3) ++#define ANX9804_AUX_RX_COMM_AUX_DEFER BIT(1) ++ ++#define ANX9804_DP_AUX_CH_CTL_1 0xe5 ++#define ANX9804_AUX_LENGTH(x) (((x - 1) & 0x0f) << 4) ++#define ANX9804_AUX_TX_COMM_MASK 0x0f ++#define ANX9804_AUX_TX_COMM_DP_TRANSACTION BIT(3) ++#define ANX9804_AUX_TX_COMM_MOT BIT(2) ++#define ANX9804_AUX_TX_COMM_READ BIT(0) ++ ++#define ANX9804_DP_AUX_ADDR_7_0 0xe6 ++#define ANX9804_DP_AUX_ADDR_15_8 0xe7 ++#define ANX9804_DP_AUX_ADDR_19_16 0xe8 ++ ++#define ANX9804_DP_AUX_CH_CTL_2 0xe9 ++#define ANX9804_ADDR_ONLY BIT(1) ++#define ANX9804_AUX_EN BIT(0) ++ ++#define ANX9804_BUF_DATA_0 0xf0 ++ ++/* Registers at i2c address 0x39 */ ++ ++#define ANX9804_DEV_IDH_REG 0x03 ++ ++#define ANX9804_POWERD_CTRL_REG 0x05 ++#define ANX9804_POWERD_AUDIO BIT(4) ++ ++#define ANX9804_RST_CTRL_REG 0x06 ++ ++#define ANX9804_RST_CTRL2_REG 0x07 ++#define ANX9804_RST_CTRL2_AUX BIT(2) ++#define ANX9804_RST_CTRL2_AC_MODE BIT(6) ++ ++#define ANX9804_VID_CTRL1_REG 0x08 ++#define ANX9804_VID_CTRL1_VID_EN BIT(7) ++#define ANX9804_VID_CTRL1_EDGE BIT(0) ++ ++#define ANX9804_VID_CTRL2_REG 0x09 ++#define ANX9804_ANALOG_DEBUG_REG1 0xdc ++#define ANX9804_ANALOG_DEBUG_REG3 0xde ++#define ANX9804_PLL_FILTER_CTRL1 0xdf ++#define ANX9804_PLL_FILTER_CTRL3 0xe1 ++#define ANX9804_PLL_FILTER_CTRL 0xe2 ++#define ANX9804_PLL_CTRL3 0xe6 ++ ++#define ANX9804_DP_INT_STA 0xf7 ++#define ANX9804_RPLY_RECEIV BIT(1) ++#define ANX9804_AUX_ERR BIT(0) diff --git a/patch/u-boot/u-boot-sunxi/08-add-anx6345-driver.patch b/patch/u-boot/u-boot-sunxi/08-add-anx6345-driver.patch new file mode 100644 index 000000000..72f71ec3d --- /dev/null +++ b/patch/u-boot/u-boot-sunxi/08-add-anx6345-driver.patch @@ -0,0 +1,466 @@ +From 713201f49e474d8d27c06db200c76c391f38c5f9 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Fri, 15 Sep 2017 23:37:29 -0700 +Subject: [PATCH] video: add anx6345 DM driver + +This is a eDP bridge similar to ANX9804, it allows to connect eDP panels +to the chips that can output only parallel signal + +Signed-off-by: Vasily Khoruzhick +--- + drivers/video/bridge/Kconfig | 8 + + drivers/video/bridge/Makefile | 1 + + drivers/video/bridge/anx6345.c | 419 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 428 insertions(+) + create mode 100644 drivers/video/bridge/anx6345.c + +diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig +index 2a3b6c4bee..765f7380b8 100644 +--- a/drivers/video/bridge/Kconfig ++++ b/drivers/video/bridge/Kconfig +@@ -25,3 +25,11 @@ config VIDEO_BRIDGE_NXP_PTN3460 + signalling) converter. It enables an LVDS LCD panel to be connected + to an eDP output device such as an SoC that lacks LVDS capability, + or where LVDS requires too many signals to route on the PCB. ++ ++config VIDEO_BRIDGE_ANALOGIX_ANX6345 ++ bool "Support Analogix ANX6345 RGB->DP bridge" ++ depends on VIDEO_BRIDGE ++ select DM_I2C ++ help ++ The Analogix ANX6345 is RGB-to-DP converter. It enables an eDP LCD ++ panel to be connected to an parallel LCD interface. +diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile +index ce731fa4ca..2a746c6f8b 100644 +--- a/drivers/video/bridge/Makefile ++++ b/drivers/video/bridge/Makefile +@@ -7,3 +7,4 @@ + obj-$(CONFIG_VIDEO_BRIDGE) += video-bridge-uclass.o + obj-$(CONFIG_VIDEO_BRIDGE_PARADE_PS862X) += ps862x.o + obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o ++obj-$(CONFIG_VIDEO_BRIDGE_ANALOGIX_ANX6345) += anx6345.o +diff --git a/drivers/video/bridge/anx6345.c b/drivers/video/bridge/anx6345.c +new file mode 100644 +index 0000000000..6bac9a51a9 +--- /dev/null ++++ b/drivers/video/bridge/anx6345.c +@@ -0,0 +1,419 @@ ++/* ++ * Copyright (C) 2017 Vasily Khoruzhick ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DP_MAX_LINK_RATE 0x001 ++#define DP_MAX_LANE_COUNT 0x002 ++#define DP_MAX_LANE_COUNT_MASK 0x1f ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++struct anx6345_priv { ++ u8 edid[EDID_SIZE]; ++}; ++ ++static int anx6345_write(struct udevice *dev, unsigned addr_off, ++ unsigned char reg_addr, unsigned char value) ++{ ++ uint8_t buf[2]; ++ struct i2c_msg msg; ++ int ret; ++ ++ msg.addr = addr_off; ++ msg.flags = 0; ++ buf[0] = reg_addr; ++ buf[1] = value; ++ msg.buf = buf; ++ msg.len = 2; ++ ret = dm_i2c_xfer(dev, &msg, 1); ++ if (ret) { ++ debug("%s: write failed, reg=%#x, value=%#x, ret=%d\n", ++ __func__, reg_addr, value, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int anx6345_read(struct udevice *dev, unsigned addr_off, ++ unsigned char reg_addr, unsigned char *value) ++{ ++ uint8_t addr, val; ++ struct i2c_msg msg[2]; ++ int ret; ++ ++ msg[0].addr = addr_off; ++ msg[0].flags = 0; ++ addr = reg_addr; ++ msg[0].buf = &addr; ++ msg[0].len = 1; ++ msg[1].addr = addr_off; ++ msg[1].flags = I2C_M_RD; ++ msg[1].buf = &val; ++ msg[1].len = 1; ++ ret = dm_i2c_xfer(dev, msg, 2); ++ if (ret) { ++ debug("%s: read failed, reg=%.2x, value=%p, ret=%d\n", ++ __func__, (int)reg_addr, value, ret); ++ return ret; ++ } ++ *value = val; ++ ++ return 0; ++} ++ ++static int anx6345_write_r0(struct udevice *dev, unsigned char reg_addr, ++ unsigned char value) ++{ ++ struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); ++ return anx6345_write(dev, chip->chip_addr, reg_addr, value); ++} ++ ++static int anx6345_read_r0(struct udevice *dev, unsigned char reg_addr, ++ unsigned char *value) ++{ ++ struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); ++ return anx6345_read(dev, chip->chip_addr, reg_addr, value); ++} ++ ++static int anx6345_write_r1(struct udevice *dev, unsigned char reg_addr, ++ unsigned char value) ++{ ++ struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); ++ return anx6345_write(dev, chip->chip_addr + 1, reg_addr, value); ++} ++ ++static int anx6345_read_r1(struct udevice *dev, unsigned char reg_addr, ++ unsigned char *value) ++{ ++ struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); ++ return anx6345_read(dev, chip->chip_addr + 1, reg_addr, value); ++} ++ ++static int anx6345_set_backlight(struct udevice *dev, int percent) ++{ ++ return -ENOSYS; ++} ++ ++static int anx6345_aux_wait(struct udevice *dev) ++{ ++ int ret = -ETIMEDOUT; ++ u8 v; ++ int retries = 1000; ++ do { ++ anx6345_read_r0(dev, ANX9804_DP_AUX_CH_CTL_2, &v); ++ if (!(v & ANX9804_AUX_EN)) { ++ ret = 0; ++ break; ++ } ++ udelay(100); ++ } while (retries--); ++ ++ if (ret) { ++ debug("%s: timed out waiting for AUX_EN to clear\n", __func__); ++ return ret; ++ } ++ ++ ret = -ETIMEDOUT; ++ retries = 1000; ++ do { ++ anx6345_read_r1(dev, ANX9804_DP_INT_STA, &v); ++ if (v & ANX9804_RPLY_RECEIV) { ++ ret = 0; ++ break; ++ } ++ udelay(100); ++ } while (retries--); ++ ++ if (ret) { ++ debug("%s: timed out waiting to receive reply\n", __func__); ++ return ret; ++ } ++ ++ /* Clear RPLY_RECEIV bit */ ++ anx6345_write_r1(dev, ANX9804_DP_INT_STA, v); ++ ++ anx6345_read_r0(dev, ANX9804_AUX_CH_STA, &v); ++ if ((v & ANX9804_AUX_STATUS_MASK) != 0) { ++ debug("AUX status: %d\n", v & ANX9804_AUX_STATUS_MASK); ++ ret = -EIO; ++ } ++ ++ return ret; ++} ++ ++static void anx6345_aux_addr(struct udevice *dev, u32 addr) ++{ ++ u8 val; ++ ++ val = addr & 0xff; ++ anx6345_write_r0(dev, ANX9804_DP_AUX_ADDR_7_0, val); ++ val = (addr >> 8) & 0xff; ++ anx6345_write_r0(dev, ANX9804_DP_AUX_ADDR_15_8, val); ++ val = (addr >> 16) & 0x0f; ++ anx6345_write_r0(dev, ANX9804_DP_AUX_ADDR_19_16, val); ++} ++ ++static int anx6345_aux_transfer(struct udevice *dev, u8 req, u32 addr, u8 *buf, size_t len) ++{ ++ int i, ret; ++ u8 ctrl1 = req; ++ u8 ctrl2 = ANX9804_AUX_EN; ++ ++ if (len > 16) ++ return -E2BIG; ++ ++ if (len) ++ ctrl1 |= ANX9804_AUX_LENGTH(len); ++ else ++ ctrl2 |= ANX9804_ADDR_ONLY; ++ ++ if (len && !(req & ANX9804_AUX_TX_COMM_READ)) { ++ for (i = 0; i < len; i++) ++ anx6345_write_r0(dev, ANX9804_BUF_DATA_0 + i, buf[i]); ++ } ++ ++ anx6345_aux_addr(dev, addr); ++ anx6345_write_r0(dev, ANX9804_DP_AUX_CH_CTL_1, ctrl1); ++ anx6345_write_r0(dev, ANX9804_DP_AUX_CH_CTL_2, ctrl2); ++ ret = anx6345_aux_wait(dev); ++ if (ret) { ++ debug("AUX transaction timed out\n"); ++ return ret; ++ } ++ ++ if (len && (req & ANX9804_AUX_TX_COMM_READ)) { ++ for (i = 0; i < len; i++) ++ anx6345_read_r0(dev, ANX9804_BUF_DATA_0 + i, &buf[i]); ++ } ++ ++ return 0; ++ ++} ++ ++static int anx6345_read_aux_i2c(struct udevice *dev, u8 chip_addr, ++ u8 offset, ++ size_t count, ++ u8 *buf) ++{ ++ int i, ret; ++ size_t cur_cnt; ++ u8 cur_offset; ++ for (i = 0; i < count; i += 16) { ++ cur_cnt = (count - i) > 16 ? 16 : count - i; ++ cur_offset = offset + i; ++ ret = anx6345_aux_transfer(dev, ANX9804_AUX_TX_COMM_MOT, ++ chip_addr, &cur_offset, 1); ++ if (ret) { ++ debug("%s: failed to set i2c offset: %d\n", __func__, ret); ++ return ret; ++ } ++ ret = anx6345_aux_transfer(dev, ANX9804_AUX_TX_COMM_READ, ++ chip_addr, buf + i, cur_cnt); ++ if (ret) { ++ debug("%s: failed to read from i2c device: %d\n", __func__, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int anx6345_read_dpcd(struct udevice *dev, u32 reg, u8 *val) ++{ ++ int ret; ++ ++ ret = anx6345_aux_transfer(dev, ++ ANX9804_AUX_TX_COMM_READ | ++ ANX9804_AUX_TX_COMM_DP_TRANSACTION, ++ reg, val, 1); ++ if (ret) { ++ debug ("Failed to read DPCD\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int anx6345_read_edid(struct udevice *dev, u8 *buf, int size) ++{ ++ struct anx6345_priv *priv = dev_get_priv(dev); ++ ++ if (size > EDID_SIZE) ++ size = EDID_SIZE; ++ memcpy(buf, priv->edid, size); ++ ++ return size; ++} ++ ++static int anx6345_attach(struct udevice *dev) ++{ ++ /* No-op */ ++ return 0; ++} ++ ++static int anx6345_enable(struct udevice *dev) ++{ ++ u8 chipid, colordepth, lanes, data_rate, c; ++ int ret, i, bpp; ++ struct display_timing timing; ++ struct anx6345_priv *priv = dev_get_priv(dev); ++ ++ /* Deassert reset and enable power */ ++ ret = video_bridge_set_active(dev, true); ++ if (ret) ++ return ret; ++ ++ /* Reset */ ++ anx6345_write_r1(dev, ANX9804_RST_CTRL_REG, 1); ++ mdelay(100); ++ anx6345_write_r1(dev, ANX9804_RST_CTRL_REG, 0); ++ ++ /* Write 0 to the powerdown reg (powerup everything) */ ++ anx6345_write_r1(dev, ANX9804_POWERD_CTRL_REG, 0); ++ ++ ret = anx6345_read_r1(dev, ANX9804_DEV_IDH_REG, &chipid); ++ if (ret) ++ debug("%s: read id failed: %d\n", __func__, ret); ++ ++ switch (chipid) { ++ case 0x63: ++ debug("ANX63xx detected.\n"); ++ break; ++ default: ++ debug("Error anx6345 chipid mismatch: %.2x\n", (int)chipid); ++ return -ENODEV; ++ } ++ ++ for (i = 0; i < 100; i++) { ++ anx6345_read_r0(dev, ANX9804_SYS_CTRL2_REG, &c); ++ anx6345_write_r0(dev, ANX9804_SYS_CTRL2_REG, c); ++ anx6345_read_r0(dev, ANX9804_SYS_CTRL2_REG, &c); ++ if ((c & ANX9804_SYS_CTRL2_CHA_STA) == 0) ++ break; ++ ++ mdelay(5); ++ } ++ if (i == 100) ++ debug("Error anx6345 clock is not stable\n"); ++ ++ /* Set a bunch of analog related register values */ ++ anx6345_write_r0(dev, ANX9804_PLL_CTRL_REG, 0x00); ++ anx6345_write_r1(dev, ANX9804_ANALOG_DEBUG_REG1, 0x70); ++ anx6345_write_r0(dev, ANX9804_LINK_DEBUG_REG, 0x30); ++ ++ /* Force HPD */ ++ anx6345_write_r0(dev, ANX9804_SYS_CTRL3_REG, ++ ANX9804_SYS_CTRL3_F_HPD | ANX9804_SYS_CTRL3_HPD_CTRL); ++ ++ /* Power up and configure lanes */ ++ anx6345_write_r0(dev, ANX9804_ANALOG_POWER_DOWN_REG, 0x00); ++ anx6345_write_r0(dev, ANX9804_TRAINING_LANE0_SET_REG, 0x00); ++ anx6345_write_r0(dev, ANX9804_TRAINING_LANE1_SET_REG, 0x00); ++ anx6345_write_r0(dev, ANX9804_TRAINING_LANE2_SET_REG, 0x00); ++ anx6345_write_r0(dev, ANX9804_TRAINING_LANE3_SET_REG, 0x00); ++ ++ /* Reset AUX CH */ ++ anx6345_write_r1(dev, ANX9804_RST_CTRL2_REG, ++ ANX9804_RST_CTRL2_AUX); ++ anx6345_write_r1(dev, ANX9804_RST_CTRL2_REG, 0); ++ ++ /* Powerdown audio and some other unused bits */ ++ anx6345_write_r1(dev, ANX9804_POWERD_CTRL_REG, ANX9804_POWERD_AUDIO); ++ anx6345_write_r0(dev, ANX9804_HDCP_CONTROL_0_REG, 0x00); ++ anx6345_write_r0(dev, 0xa7, 0x00); ++ ++ anx6345_read_aux_i2c(dev, 0x50, 0x0, EDID_SIZE, priv->edid); ++ if (edid_get_timing(priv->edid, EDID_SIZE, &timing, &bpp) != 0) { ++ debug("Failed to parse EDID\n"); ++ return -EIO; ++ } ++ debug("%s: panel found: %dx%d, bpp %d\n", __func__, ++ timing.hactive.typ, timing.vactive.typ, ++ bpp); ++ if (bpp == 6) ++ colordepth = 0x00; /* 6 bit */ ++ else ++ colordepth = 0x10; /* 8 bit */ ++ anx6345_write_r1(dev, ANX9804_VID_CTRL2_REG, colordepth); ++ ++ if (anx6345_read_dpcd(dev, DP_MAX_LINK_RATE, &data_rate)) { ++ debug("%s: Failed to DP_MAX_LINK_RATE\n", __func__); ++ return -EIO; ++ } ++ debug("%s: data_rate: %d\n", __func__, (int)data_rate); ++ if (anx6345_read_dpcd(dev, DP_MAX_LANE_COUNT, &lanes)) { ++ debug("%s: Failed to read DP_MAX_LANE_COUNT\n", __func__); ++ return -EIO; ++ } ++ lanes &= DP_MAX_LANE_COUNT_MASK; ++ debug("%s: lanes: %d\n", __func__, (int)lanes); ++ ++ /* Set data-rate / lanes */ ++ anx6345_write_r0(dev, ANX9804_LINK_BW_SET_REG, data_rate); ++ anx6345_write_r0(dev, ANX9804_LANE_COUNT_SET_REG, lanes); ++ ++ /* Link training */ ++ anx6345_write_r0(dev, ANX9804_LINK_TRAINING_CTRL_REG, ++ ANX9804_LINK_TRAINING_CTRL_EN); ++ mdelay(5); ++ for (i = 0; i < 100; i++) { ++ anx6345_read_r0(dev, ANX9804_LINK_TRAINING_CTRL_REG, &c); ++ if ((chipid == 0x63) && (c & 0x80) == 0) ++ break; ++ ++ mdelay(5); ++ } ++ if(i == 100) { ++ debug("Error anx6345 link training timeout\n"); ++ return -ENODEV; ++ } ++ ++ /* Enable */ ++ anx6345_write_r1(dev, ANX9804_VID_CTRL1_REG, ++ ANX9804_VID_CTRL1_VID_EN | ANX9804_VID_CTRL1_EDGE); ++ /* Force stream valid */ ++ anx6345_write_r0(dev, ANX9804_SYS_CTRL3_REG, ++ ANX9804_SYS_CTRL3_F_HPD | ANX9804_SYS_CTRL3_HPD_CTRL | ++ ANX9804_SYS_CTRL3_F_VALID | ANX9804_SYS_CTRL3_VALID_CTRL); ++ ++ return 0; ++} ++ ++static int anx6345_probe(struct udevice *dev) ++{ ++ if (device_get_uclass_id(dev->parent) != UCLASS_I2C) ++ return -EPROTONOSUPPORT; ++ ++ return anx6345_enable(dev); ++} ++ ++struct video_bridge_ops anx6345_ops = { ++ .attach = anx6345_attach, ++ .set_backlight = anx6345_set_backlight, ++ .read_edid = anx6345_read_edid, ++}; ++ ++static const struct udevice_id anx6345_ids[] = { ++ { .compatible = "analogix,anx6345", }, ++ { } ++}; ++ ++U_BOOT_DRIVER(analogix_anx6345) = { ++ .name = "analogix_anx6345", ++ .id = UCLASS_VIDEO_BRIDGE, ++ .of_match = anx6345_ids, ++ .probe = anx6345_probe, ++ .ops = &anx6345_ops, ++ .priv_auto_alloc_size = sizeof(struct anx6345_priv), ++}; diff --git a/patch/u-boot/u-boot-sunxi/10-video-sunxi-split-pll-code.patch b/patch/u-boot/u-boot-sunxi/10-video-sunxi-split-pll-code.patch new file mode 100644 index 000000000..ee003af30 --- /dev/null +++ b/patch/u-boot/u-boot-sunxi/10-video-sunxi-split-pll-code.patch @@ -0,0 +1,325 @@ +From 67065ec3f2ea9b4bf375f60a09b29f9210814d1e Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Sat, 16 Sep 2017 07:05:49 -0700 +Subject: [PATCH] sunxi: video: split out PLL configuration code + +It will be reused in new DM LCD driver. + +Signed-off-by: Vasily Khoruzhick +--- + arch/arm/include/asm/arch-sunxi/lcdc.h | 2 + + drivers/video/sunxi/lcdc.c | 117 ++++++++++++++++++++++++++++++- + drivers/video/sunxi/sunxi_display.c | 121 ++------------------------------- + 3 files changed, 124 insertions(+), 116 deletions(-) + +diff --git a/arch/arm/include/asm/arch-sunxi/lcdc.h b/arch/arm/include/asm/arch-sunxi/lcdc.h +index a751698b4f..5d9253aaa5 100644 +--- a/arch/arm/include/asm/arch-sunxi/lcdc.h ++++ b/arch/arm/include/asm/arch-sunxi/lcdc.h +@@ -124,5 +124,7 @@ void lcdc_tcon0_mode_set(struct sunxi_lcdc_reg * const lcdc, + void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc, + const struct display_timing *mode, + bool ext_hvsync, bool is_composite); ++void lcdc_pll_set(struct sunxi_ccm_reg * const ccm, int tcon, ++ int dotclock, int *clk_div, int *clk_double); + + #endif /* _LCDC_H */ +diff --git a/drivers/video/sunxi/lcdc.c b/drivers/video/sunxi/lcdc.c +index 7d215b713e..023a30cb1e 100644 +--- a/drivers/video/sunxi/lcdc.c ++++ b/drivers/video/sunxi/lcdc.c +@@ -10,6 +10,7 @@ + + #include + ++#include + #include + #include + +@@ -100,7 +101,7 @@ void lcdc_tcon0_mode_set(struct sunxi_lcdc_reg * const lcdc, + writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) | + SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v); + +-#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL ++#if defined(CONFIG_VIDEO_LCD_IF_PARALLEL) || defined(CONFIG_VIDEO_DE2) + writel(SUNXI_LCDC_X(mode->hsync_len.typ) | + SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon0_timing_sync); + +@@ -207,3 +208,117 @@ void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc, + SUNXI_LCDC_MUX_CTRL_SRC0(1)); + #endif + } ++ ++void lcdc_pll_set(struct sunxi_ccm_reg *ccm, int tcon, int dotclock, ++ int *clk_div, int *clk_double) ++{ ++ int value, n, m, min_m, max_m, diff; ++ int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF; ++ int best_double = 0; ++ bool use_mipi_pll = false; ++ ++ if (tcon == 0) { ++#if defined(CONFIG_VIDEO_LCD_IF_PARALLEL) || defined(CONFIG_SUNXI_DE2) ++ min_m = 6; ++ max_m = 127; ++#endif ++#ifdef CONFIG_VIDEO_LCD_IF_LVDS ++ min_m = max_m = 7; ++#endif ++ } else { ++ min_m = 1; ++ max_m = 15; ++ } ++ ++ /* ++ * Find the lowest divider resulting in a matching clock, if there ++ * is no match, pick the closest lower clock, as monitors tend to ++ * not sync to higher frequencies. ++ */ ++ for (m = min_m; m <= max_m; m++) { ++#ifndef CONFIG_SUNXI_DE2 ++ n = (m * dotclock) / 3000; ++ ++ if ((n >= 9) && (n <= 127)) { ++ value = (3000 * n) / m; ++ diff = dotclock - value; ++ if (diff < best_diff) { ++ best_diff = diff; ++ best_m = m; ++ best_n = n; ++ best_double = 0; ++ } ++ } ++ ++ /* These are just duplicates */ ++ if (!(m & 1)) ++ continue; ++#endif ++ ++ /* No double clock on DE2 */ ++ n = (m * dotclock) / 6000; ++ if ((n >= 9) && (n <= 127)) { ++ value = (6000 * n) / m; ++ diff = dotclock - value; ++ if (diff < best_diff) { ++ best_diff = diff; ++ best_m = m; ++ best_n = n; ++ best_double = 1; ++ } ++ } ++ } ++ ++#ifdef CONFIG_MACH_SUN6I ++ /* ++ * Use the MIPI pll if we've been unable to find any matching setting ++ * for PLL3, this happens with high dotclocks because of min_m = 6. ++ */ ++ if (tcon == 0 && best_n == 0) { ++ use_mipi_pll = true; ++ best_m = 6; /* Minimum m for tcon0 */ ++ } ++ ++ if (use_mipi_pll) { ++ clock_set_pll3(297000000); /* Fix the video pll at 297 MHz */ ++ clock_set_mipi_pll(best_m * dotclock * 1000); ++ debug("dotclock: %dkHz = %dkHz via mipi pll\n", ++ dotclock, clock_get_mipi_pll() / best_m / 1000); ++ } else ++#endif ++ { ++ clock_set_pll3(best_n * 3000000); ++ debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n", ++ dotclock, ++ (best_double + 1) * clock_get_pll3() / best_m / 1000, ++ best_double + 1, best_n, best_m); ++ } ++ ++ if (tcon == 0) { ++ u32 pll; ++ ++ if (use_mipi_pll) ++ pll = CCM_LCD_CH0_CTRL_MIPI_PLL; ++ else if (best_double) ++ pll = CCM_LCD_CH0_CTRL_PLL3_2X; ++ else ++ pll = CCM_LCD_CH0_CTRL_PLL3; ++ ++ writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll, ++ &ccm->lcd0_clk_cfg); ++ } ++#ifndef CONFIG_SUNXI_DE2 ++ else { ++ writel(CCM_LCD_CH1_CTRL_GATE | ++ (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X : ++ CCM_LCD_CH1_CTRL_PLL3) | ++ CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg); ++ if (sunxi_is_composite()) ++ setbits_le32(&ccm->lcd0_ch1_clk_cfg, ++ CCM_LCD_CH1_CTRL_HALF_SCLK1); ++ } ++#endif ++ ++ *clk_div = best_m; ++ *clk_double = best_double; ++} +diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c +index de768ba94a..f3db125305 100644 +--- a/drivers/video/sunxi/sunxi_display.c ++++ b/drivers/video/sunxi/sunxi_display.c +@@ -515,119 +515,6 @@ static void sunxi_composer_enable(void) + setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START); + } + +-/* +- * LCDC, what allwinner calls a CRTC, so timing controller and serializer. +- */ +-static void sunxi_lcdc_pll_set(int tcon, int dotclock, +- int *clk_div, int *clk_double) +-{ +- struct sunxi_ccm_reg * const ccm = +- (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; +- int value, n, m, min_m, max_m, diff; +- int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF; +- int best_double = 0; +- bool use_mipi_pll = false; +- +- if (tcon == 0) { +-#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL +- min_m = 6; +- max_m = 127; +-#endif +-#ifdef CONFIG_VIDEO_LCD_IF_LVDS +- min_m = max_m = 7; +-#endif +- } else { +- min_m = 1; +- max_m = 15; +- } +- +- /* +- * Find the lowest divider resulting in a matching clock, if there +- * is no match, pick the closest lower clock, as monitors tend to +- * not sync to higher frequencies. +- */ +- for (m = min_m; m <= max_m; m++) { +- n = (m * dotclock) / 3000; +- +- if ((n >= 9) && (n <= 127)) { +- value = (3000 * n) / m; +- diff = dotclock - value; +- if (diff < best_diff) { +- best_diff = diff; +- best_m = m; +- best_n = n; +- best_double = 0; +- } +- } +- +- /* These are just duplicates */ +- if (!(m & 1)) +- continue; +- +- n = (m * dotclock) / 6000; +- if ((n >= 9) && (n <= 127)) { +- value = (6000 * n) / m; +- diff = dotclock - value; +- if (diff < best_diff) { +- best_diff = diff; +- best_m = m; +- best_n = n; +- best_double = 1; +- } +- } +- } +- +-#ifdef CONFIG_MACH_SUN6I +- /* +- * Use the MIPI pll if we've been unable to find any matching setting +- * for PLL3, this happens with high dotclocks because of min_m = 6. +- */ +- if (tcon == 0 && best_n == 0) { +- use_mipi_pll = true; +- best_m = 6; /* Minimum m for tcon0 */ +- } +- +- if (use_mipi_pll) { +- clock_set_pll3(297000000); /* Fix the video pll at 297 MHz */ +- clock_set_mipi_pll(best_m * dotclock * 1000); +- debug("dotclock: %dkHz = %dkHz via mipi pll\n", +- dotclock, clock_get_mipi_pll() / best_m / 1000); +- } else +-#endif +- { +- clock_set_pll3(best_n * 3000000); +- debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n", +- dotclock, +- (best_double + 1) * clock_get_pll3() / best_m / 1000, +- best_double + 1, best_n, best_m); +- } +- +- if (tcon == 0) { +- u32 pll; +- +- if (use_mipi_pll) +- pll = CCM_LCD_CH0_CTRL_MIPI_PLL; +- else if (best_double) +- pll = CCM_LCD_CH0_CTRL_PLL3_2X; +- else +- pll = CCM_LCD_CH0_CTRL_PLL3; +- +- writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll, +- &ccm->lcd0_ch0_clk_cfg); +- } else { +- writel(CCM_LCD_CH1_CTRL_GATE | +- (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X : +- CCM_LCD_CH1_CTRL_PLL3) | +- CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg); +- if (sunxi_is_composite()) +- setbits_le32(&ccm->lcd0_ch1_clk_cfg, +- CCM_LCD_CH1_CTRL_HALF_SCLK1); +- } +- +- *clk_div = best_m; +- *clk_double = best_double; +-} +- + static void sunxi_lcdc_init(void) + { + struct sunxi_ccm_reg * const ccm = +@@ -754,6 +641,8 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, + { + struct sunxi_lcdc_reg * const lcdc = + (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; ++ struct sunxi_ccm_reg * const ccm = ++ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + int clk_div, clk_double, pin; + struct display_timing timing; + +@@ -773,7 +662,7 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, + #endif + } + +- sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double); ++ lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double); + + sunxi_ctfb_mode_to_display_timing(mode, &timing); + lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac, +@@ -787,6 +676,8 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, + { + struct sunxi_lcdc_reg * const lcdc = + (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; ++ struct sunxi_ccm_reg * const ccm = ++ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct display_timing timing; + + sunxi_ctfb_mode_to_display_timing(mode, &timing); +@@ -798,7 +689,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, + sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0); + } + +- sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double); ++ lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double); + } + #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */ + diff --git a/patch/u-boot/u-boot-sunxi/11-de2-add-lcd-support.patch b/patch/u-boot/u-boot-sunxi/11-de2-add-lcd-support.patch new file mode 100644 index 000000000..a8487a991 --- /dev/null +++ b/patch/u-boot/u-boot-sunxi/11-de2-add-lcd-support.patch @@ -0,0 +1,223 @@ +From 32f419400bd74b4292fc2117091f9fb999941a28 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Sat, 16 Sep 2017 07:37:50 -0700 +Subject: [PATCH] sunxi: video: add LCD support to DE2 driver + +Extend DE2 driver with LCD support. Tested on Pinebook which is based +on A64 and has ANX6345 eDP bridge with eDP panel connected to it. + +Signed-off-by: Vasily Khoruzhick +--- + arch/arm/mach-sunxi/Kconfig | 2 +- + drivers/video/sunxi/Makefile | 2 +- + drivers/video/sunxi/sunxi_de2.c | 17 +++++ + drivers/video/sunxi/sunxi_lcd.c | 149 ++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 168 insertions(+), 2 deletions(-) + create mode 100644 drivers/video/sunxi/sunxi_lcd.c + +diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig +index 2309f59999..06d697e3a7 100644 +--- a/arch/arm/mach-sunxi/Kconfig ++++ b/arch/arm/mach-sunxi/Kconfig +@@ -680,7 +680,7 @@ config VIDEO_LCD_MODE + + config VIDEO_LCD_DCLK_PHASE + int "LCD panel display clock phase" +- depends on VIDEO ++ depends on VIDEO || DM_VIDEO + default 1 + ---help--- + Select LCD panel display clock phase shift, range 0-3. +diff --git a/drivers/video/sunxi/Makefile b/drivers/video/sunxi/Makefile +index 0d64c2021f..8c91766c24 100644 +--- a/drivers/video/sunxi/Makefile ++++ b/drivers/video/sunxi/Makefile +@@ -6,4 +6,4 @@ + # + + obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o lcdc.o tve_common.o ../videomodes.o +-obj-$(CONFIG_VIDEO_DE2) += sunxi_de2.o sunxi_dw_hdmi.o lcdc.o ../dw_hdmi.o ++obj-$(CONFIG_VIDEO_DE2) += sunxi_de2.o sunxi_dw_hdmi.o lcdc.o ../dw_hdmi.o sunxi_lcd.o +diff --git a/drivers/video/sunxi/sunxi_de2.c b/drivers/video/sunxi/sunxi_de2.c +index ee67764ac5..a838bbacd1 100644 +--- a/drivers/video/sunxi/sunxi_de2.c ++++ b/drivers/video/sunxi/sunxi_de2.c +@@ -233,6 +233,23 @@ static int sunxi_de2_probe(struct udevice *dev) + return 0; + + ret = uclass_find_device_by_name(UCLASS_DISPLAY, ++ "sunxi_lcd", &disp); ++ if (!ret) { ++ int mux; ++ ++ mux = 0; ++ ++ ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, mux, ++ false); ++ if (!ret) { ++ video_set_flush_dcache(dev, 1); ++ return 0; ++ } ++ } ++ ++ debug("%s: lcd display not found (ret=%d)\n", __func__, ret); ++ ++ ret = uclass_find_device_by_name(UCLASS_DISPLAY, + "sunxi_dw_hdmi", &disp); + if (!ret) { + int mux; +diff --git a/drivers/video/sunxi/sunxi_lcd.c b/drivers/video/sunxi/sunxi_lcd.c +new file mode 100644 +index 0000000000..b8e4b0e344 +--- /dev/null ++++ b/drivers/video/sunxi/sunxi_lcd.c +@@ -0,0 +1,149 @@ ++/* ++ * Allwinner LCD driver ++ * ++ * (C) Copyright 2017 Vasily Khoruzhick ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct sunxi_lcd_priv { ++ struct display_timing timing; ++ int panel_bpp; ++}; ++ ++static void sunxi_lcdc_config_pinmux(void) ++{ ++#ifdef CONFIG_MACH_SUN50I ++ int pin; ++ for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(21); pin++) { ++ sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0); ++ sunxi_gpio_set_drv(pin, 3); ++ } ++#endif ++} ++ ++static int sunxi_lcd_enable(struct udevice *dev, int bpp, ++ const struct display_timing *edid) ++{ ++ struct sunxi_ccm_reg * const ccm = ++ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; ++ struct sunxi_lcdc_reg * const lcdc = ++ (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; ++ struct sunxi_lcd_priv *priv = dev_get_priv(dev); ++ struct udevice *backlight; ++ int clk_div, clk_double, ret; ++ ++ /* Reset off */ ++ setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0); ++ /* Clock on */ ++ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0); ++ ++ lcdc_init(lcdc); ++ sunxi_lcdc_config_pinmux(); ++ lcdc_pll_set(ccm, 0, edid->pixelclock.typ / 1000, ++ &clk_div, &clk_double); ++ lcdc_tcon0_mode_set(lcdc, edid, clk_div, false, ++ priv->panel_bpp, CONFIG_VIDEO_LCD_DCLK_PHASE); ++ lcdc_enable(lcdc, priv->panel_bpp); ++ ++ ret = uclass_get_device(UCLASS_PANEL_BACKLIGHT, 0, &backlight); ++ if (!ret) ++ backlight_enable(backlight); ++ ++ return 0; ++} ++ ++static int sunxi_lcd_read_timing(struct udevice *dev, ++ struct display_timing *timing) ++{ ++ struct sunxi_lcd_priv *priv = dev_get_priv(dev); ++ memcpy(timing, &priv->timing, sizeof(struct display_timing)); ++ ++ return 0; ++} ++ ++static int sunxi_lcd_probe(struct udevice *dev) ++{ ++ struct udevice *cdev; ++ struct sunxi_lcd_priv *priv = dev_get_priv(dev); ++ int ret; ++ int node, timing_node, val; ++ ++#ifdef CONFIG_VIDEO_BRIDGE ++ /* Try to get timings from bridge first */ ++ ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &cdev); ++ if (!ret) { ++ u8 edid[EDID_SIZE]; ++ int channel_bpp; ++ ++ ret = video_bridge_attach(cdev); ++ if (ret) { ++ debug("video bridge attach failed: %d\n", ret); ++ return ret; ++ } ++ ret = video_bridge_read_edid(cdev, edid, EDID_SIZE); ++ if (ret > 0) { ++ ret = edid_get_timing(edid, ret, &priv->timing, &channel_bpp); ++ priv->panel_bpp = channel_bpp * 3; ++ if (!ret) ++ return ret; ++ } ++ } ++#endif ++ ++ /* Fallback to timings from DT if there's no bridge or ++ * if reading EDID failed ++ */ ++ ret = uclass_get_device(UCLASS_PANEL, 0, &cdev); ++ if (ret) { ++ debug("video panel not found: %d\n", ret); ++ return ret; ++ } ++ ++ if (fdtdec_decode_display_timing(gd->fdt_blob, dev_of_offset(cdev), ++ 0, &priv->timing)) { ++ debug("%s: Failed to decode display timing\n", __func__); ++ return -EINVAL; ++ } ++ timing_node = fdt_subnode_offset(gd->fdt_blob, dev_of_offset(cdev), ++ "display-timings"); ++ node = fdt_first_subnode(gd->fdt_blob, timing_node); ++ val = fdtdec_get_int(gd->fdt_blob, node, "bits-per-pixel", -1); ++ if (val != -1) ++ priv->panel_bpp = val; ++ else ++ priv->panel_bpp = 18; ++ ++ return 0; ++} ++ ++static const struct dm_display_ops sunxi_lcd_ops = { ++ .read_timing = sunxi_lcd_read_timing, ++ .enable = sunxi_lcd_enable, ++}; ++ ++U_BOOT_DRIVER(sunxi_lcd) = { ++ .name = "sunxi_lcd", ++ .id = UCLASS_DISPLAY, ++ .ops = &sunxi_lcd_ops, ++ .probe = sunxi_lcd_probe, ++ .priv_auto_alloc_size = sizeof(struct sunxi_lcd_priv), ++}; ++ ++#ifdef CONFIG_MACH_SUN50I ++U_BOOT_DEVICE(sunxi_lcd) = { ++ .name = "sunxi_lcd" ++}; ++#endif diff --git a/patch/u-boot/u-boot-sunxi/add-pinebook-defconfig.patch b/patch/u-boot/u-boot-sunxi/12-add-pinebook-dt-and-defconfig.patch similarity index 53% rename from patch/u-boot/u-boot-sunxi/add-pinebook-defconfig.patch rename to patch/u-boot/u-boot-sunxi/12-add-pinebook-dt-and-defconfig.patch index 68e8ad6c2..c9fb44c45 100644 --- a/patch/u-boot/u-boot-sunxi/add-pinebook-defconfig.patch +++ b/patch/u-boot/u-boot-sunxi/12-add-pinebook-dt-and-defconfig.patch @@ -1,35 +1,27 @@ -diff --git a/configs/pinebook-a64_defconfig b/configs/pinebook-a64_defconfig -new file mode 100644 -index 0000000000..ce4e36e5c3 ---- /dev/null -+++ b/configs/pinebook-a64_defconfig -@@ -0,0 +1,21 @@ -+CONFIG_ARM=y -+CONFIG_ARCH_SUNXI=y -+CONFIG_MACH_SUN50I=y -+CONFIG_RESERVE_ALLWINNER_BOOT0_HEADER=y -+CONFIG_SUNXI_DRAM_LPDDR3_STOCK=y -+CONFIG_DRAM_CLK=552 -+CONFIG_DRAM_ZQ=3881949 -+CONFIG_DRAM_ODT_EN=y -+CONFIG_MMC0_CD_PIN="" -+CONFIG_MMC_SUNXI_SLOT_EXTRA=2 -+CONFIG_DEFAULT_DEVICE_TREE="sun50i-a64-pinebook" -+# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set -+CONFIG_SPL=y -+# CONFIG_CMD_IMLS is not set -+# CONFIG_CMD_FLASH is not set -+# CONFIG_CMD_FPGA is not set -+# CONFIG_SPL_DOS_PARTITION is not set -+# CONFIG_SPL_ISO_PARTITION is not set -+# CONFIG_SPL_EFI_PARTITION is not set -+CONFIG_SUN8I_EMAC=y -+CONFIG_USB_EHCI_HCD=y +From 0e9350e3ff11c91eb6959d0d4589a4cc5e576e35 Mon Sep 17 00:00:00 2001 +From: Vasily Khoruzhick +Date: Sat, 16 Sep 2017 07:44:11 -0700 +Subject: [PATCH] sunxi: add support for Pinebook + +Pinebook is a laptop produced by Pine64, with USB-connected keyboard, +I2C-connected touchpad and an eDP LCD panel connected via a RGB-eDP +bridge by Analogix. + +Signed-off-by: Icenowy Zheng +Signed-off-by: Vasily Khoruzhick +--- + arch/arm/dts/Makefile | 3 +- + arch/arm/dts/sun50i-a64-pinebook.dts | 114 +++++++++++++++++++++++++++++++++++ + configs/pinebook_defconfig | 36 +++++++++++ + 3 files changed, 152 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/dts/sun50i-a64-pinebook.dts + create mode 100644 configs/pinebook_defconfig + diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile -index 762429c..98f57e0 100644 +index fee4680057..130d1944be 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile -@@ -338,7 +338,8 @@ dtb-$(CONFIG_MACH_SUN50I) += \ +@@ -342,7 +342,8 @@ dtb-$(CONFIG_MACH_SUN50I) += \ sun50i-a64-olinuxino.dtb \ sun50i-a64-orangepi-win.dtb \ sun50i-a64-pine64-plus.dtb \ @@ -41,10 +33,10 @@ index 762429c..98f57e0 100644 sun9i-a80-cubieboard4.dtb \ diff --git a/arch/arm/dts/sun50i-a64-pinebook.dts b/arch/arm/dts/sun50i-a64-pinebook.dts new file mode 100644 -index 0000000..97d46d3 +index 0000000000..17d4c598b8 --- /dev/null +++ b/arch/arm/dts/sun50i-a64-pinebook.dts -@@ -0,0 +1,50 @@ +@@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 ARM Ltd. + * @@ -88,10 +80,114 @@ index 0000000..97d46d3 + */ + +#include "sun50i-a64-pine64.dts" ++#include + +/ { -+ model = "Pinebook A64"; -+ compatible = "pine64,pine64-pinebook", "allwinner,sun50i-a64"; ++ model = "Pinebook"; ++ compatible = "pine64,pinebook", "allwinner,sun50i-a64"; + -+ /* TODO: Everything */ ++ aliases { ++ serial0 = &uart0; ++ i2c0 = "/i2c_gpio@0"; ++ }; ++ ++ backlight: backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm 0 50000 PWM_POLARITY_INVERTED>; ++ brightness-levels = <0 10 20 30 40 50 60 70 100>; ++ default-brightness-level = <3>; ++ enable-gpios = <&pio 3 23 GPIO_ACTIVE_HIGH>; /* PD23 */ ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ memory { ++ reg = <0x40000000 0x40000000>; ++ }; ++ ++ soc { ++ i2c_gpio@0 { ++ compatible = "i2c-gpio"; ++ gpios = <&r_pio 0 9 GPIO_ACTIVE_HIGH>, /* sda - PL9 */ ++ <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* scl - PL8 */ ++ i2c-gpio,sda-open-drain; ++ i2c-gpio,scl-open-drain; ++ i2c-gpio,delay-us = <2>; /* ~100 kHz */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ anx6345: edp-bridge@38 { ++ compatible = "analogix,anx6345"; ++ reg = <0x38>; ++ sleep-gpios = <&pio 7 6 GPIO_ACTIVE_LOW>; /* PH6 */ ++ reset-gpios = <&pio 3 24 GPIO_ACTIVE_LOW>; /* PD24 */ ++ status = "okay"; ++ ++ /* ++ ports { ++ port@0 { ++ bridge_out: endpoint { ++ remote-endpoint = <&panel_in>; ++ }; ++ }; ++ ++ port@1 { ++ bridge_in: endpoint { ++ remote-endpoint = <&rgb_out>; ++ }; ++ }; ++ }; ++ */ ++ }; ++ }; ++ }; +}; ++ ++&pwm { ++ status = "okay"; ++}; +diff --git a/configs/pinebook-a64_defconfig b/configs/pinebook-a64_defconfig +new file mode 100644 +index 0000000000..e90c863f76 +--- /dev/null ++++ b/configs/pinebook-a64_defconfig +@@ -0,0 +1,36 @@ ++CONFIG_ARM=y ++CONFIG_ARCH_SUNXI=y ++CONFIG_MACH_SUN50I=y ++CONFIG_RESERVE_ALLWINNER_BOOT0_HEADER=y ++CONFIG_SUNXI_DRAM_LPDDR3_STOCK=y ++CONFIG_DRAM_CLK=552 ++CONFIG_DRAM_ZQ=3881949 ++CONFIG_DRAM_ODT_EN=y ++CONFIG_MMC_SUNXI_SLOT_EXTRA=2 ++CONFIG_DEFAULT_DEVICE_TREE="sun50i-a64-pinebook" ++# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set ++CONFIG_SPL=y ++# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set ++# CONFIG_SPL_LEGACY_IMAGE_SUPPORT is not set ++CONFIG_SPL_ATF_SUPPORT=y ++CONFIG_SPL_ATF_TEXT_BASE=0x44000 ++# CONFIG_CMD_IMLS is not set ++# CONFIG_CMD_FLASH is not set ++# CONFIG_CMD_FPGA is not set ++CONFIG_CMD_I2C=y ++# CONFIG_SPL_DOS_PARTITION is not set ++# CONFIG_SPL_ISO_PARTITION is not set ++# CONFIG_SPL_EFI_PARTITION is not set ++CONFIG_I2C_SET_DEFAULT_BUS_NUM=y ++CONFIG_DM_I2C_GPIO=y ++CONFIG_SPL_SPI_SUNXI=y ++CONFIG_DM_REGULATOR=y ++CONFIG_AXP_DLDO2_VOLT=2500 ++CONFIG_AXP_FLDO1_VOLT=1200 ++CONFIG_AXP_SW_ON=y ++CONFIG_DM_PWM=y ++CONFIG_PWM_SUNXI=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE=y ++CONFIG_VIDEO_BRIDGE=y ++CONFIG_VIDEO_BRIDGE_ANALOGIX_ANX6345=y