| @@ -0,0 +1,21 @@ | |||
| diff --git a/trust.ini b/trust.ini | |||
| new file mode 100644 | |||
| index 0000000..4af021a | |||
| --- /dev/null | |||
| +++ b/trust.ini | |||
| @@ -0,0 +1,15 @@ | |||
| +[VERSION] | |||
| +MAJOR=1 | |||
| +MINOR=0 | |||
| +[BL30_OPTION] | |||
| +SEC=0 | |||
| +[BL31_OPTION] | |||
| +SEC=1 | |||
| +PATH=./build/rk3399/debug/bl31/bl31.elf | |||
| +ADDR=0x10000 | |||
| +[BL32_OPTION] | |||
| +SEC=0 | |||
| +[BL33_OPTION] | |||
| +SEC=0 | |||
| +[OUTPUT] | |||
| +PATH=trust.bin | |||
| @@ -16,10 +16,15 @@ let | |||
| src = fetchFromGitHub { | |||
| owner = "ARM-software"; | |||
| repo = "arm-trusted-firmware"; | |||
| rev = "v${version}"; | |||
| sha256 = "113mcf1hwwl0i90cqh08lywxs1bfbg0nwqibay9wlkmx1a5v0bnj"; | |||
| rev = "73740d98d99cb740ff67e188b6a7c1db816bf9f4"; | |||
| sha256 = "0x68hxlfyw6v6szp4wyfxsi9plw34x91f4mzdjibbj4dg42hfvag"; | |||
| }; | |||
| patches = [ | |||
| ./atf-add-trust-ini.patch | |||
| ./enable-logging.patch | |||
| ]; | |||
| depsBuildBuild = [ buildPackages.stdenv.cc ]; | |||
| # For Cortex-M0 firmware in RK3399 | |||
| @@ -0,0 +1,31 @@ | |||
| diff --git a/plat/rockchip/rk3399/rk3399_def.h b/plat/rockchip/rk3399/rk3399_def.h | |||
| index ba83242..8d6ecfb 100644 | |||
| --- a/plat/rockchip/rk3399/rk3399_def.h | |||
| +++ b/plat/rockchip/rk3399/rk3399_def.h | |||
| @@ -17,7 +17,7 @@ | |||
| /************************************************************************** | |||
| * UART related constants | |||
| **************************************************************************/ | |||
| -#define RK3399_BAUDRATE 115200 | |||
| +#define RK3399_BAUDRATE 1500000 | |||
| #define RK3399_UART_CLOCK 24000000 | |||
| /****************************************************************************** | |||
| diff --git a/plat/rockchip/common/bl31_plat_setup.c b/plat/rockchip/common/bl31_plat_setup.c | |||
| index c4a0359..22d07e0 100644 | |||
| --- a/plat/rockchip/common/bl31_plat_setup.c | |||
| +++ b/plat/rockchip/common/bl31_plat_setup.c | |||
| @@ -61,10 +61,13 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, | |||
| params_early_setup(arg1); | |||
| - if (rockchip_get_uart_base() != 0) | |||
| + if (rockchip_get_uart_base() != 0) { | |||
| console_16550_register(rockchip_get_uart_base(), | |||
| rockchip_get_uart_clock(), | |||
| rockchip_get_uart_baudrate(), &console); | |||
| + console_set_scope(&console, | |||
| + CONSOLE_FLAG_BOOT | CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH); | |||
| + } | |||
| VERBOSE("bl31_setup\n"); | |||
| @@ -0,0 +1,189 @@ | |||
| diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile | |||
| index 9900b442..afa9ec81 100644 | |||
| --- a/arch/arm/dts/Makefile | |||
| +++ b/arch/arm/dts/Makefile | |||
| @@ -128,6 +128,7 @@ dtb-$(CONFIG_ROCKCHIP_RK3399) += \ | |||
| rk3399-nanopc-t4.dtb \ | |||
| rk3399-nanopi-m4.dtb \ | |||
| rk3399-nanopi-m4-2gb.dtb \ | |||
| + rk3399-nanopi-m4v2.dtb \ | |||
| rk3399-nanopi-neo4.dtb \ | |||
| rk3399-orangepi.dtb \ | |||
| rk3399-pinebook-pro.dtb \ | |||
| diff --git a/arch/arm/dts/rk3399-nanopi-m4v2-u-boot.dtsi b/arch/arm/dts/rk3399-nanopi-m4v2-u-boot.dtsi | |||
| new file mode 100644 | |||
| index 00000000..4b775f6d | |||
| --- /dev/null | |||
| +++ b/arch/arm/dts/rk3399-nanopi-m4v2-u-boot.dtsi | |||
| @@ -0,0 +1,17 @@ | |||
| +// SPDX-License-Identifier: GPL-2.0+ | |||
| +/* | |||
| + * Copyright (C) 2019 Jagan Teki <jagan@amarulasolutions.com> | |||
| + */ | |||
| + | |||
| +#include "rk3399-nanopi4-u-boot.dtsi" | |||
| +#include "rk3399-sdram-lpddr4-100.dtsi" | |||
| + | |||
| +/ { | |||
| + chosen { | |||
| + u-boot,spl-boot-order = &sdmmc, &sdhci; | |||
| + }; | |||
| +}; | |||
| + | |||
| +&sdio0 { | |||
| + status = "disabled"; | |||
| +}; | |||
| diff --git a/arch/arm/dts/rk3399-nanopi-m4v2.dts b/arch/arm/dts/rk3399-nanopi-m4v2.dts | |||
| new file mode 100644 | |||
| index 00000000..eac77f2a | |||
| --- /dev/null | |||
| +++ b/arch/arm/dts/rk3399-nanopi-m4v2.dts | |||
| @@ -0,0 +1,66 @@ | |||
| +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) | |||
| +/* | |||
| + * FriendlyElec NanoPi M4 board device tree source | |||
| + * | |||
| + * Copyright (c) 2018 FriendlyElec Computer Tech. Co., Ltd. | |||
| + * (http://www.friendlyarm.com) | |||
| + * | |||
| + * Copyright (c) 2018 Collabora Ltd. | |||
| + * Copyright (c) 2019 Arm Ltd. | |||
| + */ | |||
| + | |||
| +/dts-v1/; | |||
| +#include "rk3399-nanopi4.dtsi" | |||
| + | |||
| +/ { | |||
| + model = "FriendlyElec NanoPi M4V2"; | |||
| + compatible = "friendlyarm,nanopi-m4", "rockchip,rk3399"; | |||
| + | |||
| + vdd_5v: vdd-5v { | |||
| + compatible = "regulator-fixed"; | |||
| + regulator-name = "vdd_5v"; | |||
| + regulator-always-on; | |||
| + regulator-boot-on; | |||
| + }; | |||
| + | |||
| + vcc5v0_core: vcc5v0-core { | |||
| + compatible = "regulator-fixed"; | |||
| + regulator-name = "vcc5v0_core"; | |||
| + regulator-always-on; | |||
| + regulator-boot-on; | |||
| + vin-supply = <&vdd_5v>; | |||
| + }; | |||
| + | |||
| + vcc5v0_usb1: vcc5v0-usb1 { | |||
| + compatible = "regulator-fixed"; | |||
| + regulator-name = "vcc5v0_usb1"; | |||
| + regulator-always-on; | |||
| + regulator-boot-on; | |||
| + vin-supply = <&vcc5v0_sys>; | |||
| + }; | |||
| + | |||
| + vcc5v0_usb2: vcc5v0-usb2 { | |||
| + compatible = "regulator-fixed"; | |||
| + regulator-name = "vcc5v0_usb2"; | |||
| + regulator-always-on; | |||
| + regulator-boot-on; | |||
| + vin-supply = <&vcc5v0_sys>; | |||
| + }; | |||
| +}; | |||
| + | |||
| +&vcc3v3_sys { | |||
| + vin-supply = <&vcc5v0_core>; | |||
| +}; | |||
| + | |||
| +&u2phy0_host { | |||
| + phy-supply = <&vcc5v0_usb1>; | |||
| +}; | |||
| + | |||
| +&u2phy1_host { | |||
| + phy-supply = <&vcc5v0_usb2>; | |||
| +}; | |||
| + | |||
| +&vbus_typec { | |||
| + regulator-always-on; | |||
| + vin-supply = <&vdd_5v>; | |||
| +}; | |||
| diff --git a/configs/nanopi-m4v2-rk3399_defconfig b/configs/nanopi-m4v2-rk3399_defconfig | |||
| new file mode 100644 | |||
| index 00000000..dd26f73d | |||
| --- /dev/null | |||
| +++ b/configs/nanopi-m4v2-rk3399_defconfig | |||
| @@ -0,0 +1,76 @@ | |||
| +CONFIG_ARM=y | |||
| +CONFIG_ARCH_ROCKCHIP=y | |||
| +CONFIG_SYS_TEXT_BASE=0x00200000 | |||
| +CONFIG_ENV_OFFSET=0x3F8000 | |||
| +CONFIG_ROCKCHIP_RK3399=y | |||
| +CONFIG_TARGET_EVB_RK3399=y | |||
| +CONFIG_NR_DRAM_BANKS=1 | |||
| +CONFIG_DEBUG_UART_BASE=0xFF1A0000 | |||
| +CONFIG_DEBUG_UART_CLOCK=24000000 | |||
| +CONFIG_DEBUG_UART=y | |||
| +CONFIG_DEFAULT_FDT_FILE="rockchip/rk3399-nanopi-m4v2.dtb" | |||
| +CONFIG_MISC_INIT_R=y | |||
| +# CONFIG_DISPLAY_CPUINFO is not set | |||
| +CONFIG_DISPLAY_BOARDINFO_LATE=y | |||
| +# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set | |||
| +CONFIG_SPL_STACK_R=y | |||
| +CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x10000 | |||
| +CONFIG_TPL=y | |||
| +CONFIG_CMD_BOOTZ=y | |||
| +CONFIG_CMD_GPT=y | |||
| +CONFIG_CMD_MMC=y | |||
| +CONFIG_CMD_USB=y | |||
| +CONFIG_CMD_PCI=y | |||
| +# CONFIG_CMD_SETEXPR is not set | |||
| +CONFIG_CMD_TIME=y | |||
| +CONFIG_SPL_OF_CONTROL=y | |||
| +CONFIG_DEFAULT_DEVICE_TREE="rk3399-nanopi-m4v2" | |||
| +CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents" | |||
| +CONFIG_ENV_IS_IN_MMC=y | |||
| +CONFIG_SYS_RELOC_GD_ENV_ADDR=y | |||
| +CONFIG_ROCKCHIP_GPIO=y | |||
| +CONFIG_SYS_I2C_ROCKCHIP=y | |||
| +CONFIG_MMC_DW=y | |||
| +CONFIG_MMC_DW_ROCKCHIP=y | |||
| +CONFIG_MMC_SDHCI=y | |||
| +CONFIG_MMC_SDHCI_ROCKCHIP=y | |||
| +CONFIG_DM_ETH=y | |||
| +CONFIG_ETH_DESIGNWARE=y | |||
| +CONFIG_GMAC_ROCKCHIP=y | |||
| +CONFIG_NVME=y | |||
| +CONFIG_PCI=y | |||
| +CONFIG_PMIC_RK8XX=y | |||
| +CONFIG_REGULATOR_PWM=y | |||
| +CONFIG_REGULATOR_RK8XX=y | |||
| +CONFIG_PWM_ROCKCHIP=y | |||
| +CONFIG_RAM_RK3399_LPDDR4=y | |||
| +CONFIG_DM_RESET=y | |||
| +CONFIG_BAUDRATE=1500000 | |||
| +CONFIG_DEBUG_UART_SHIFT=2 | |||
| +CONFIG_SYSRESET=y | |||
| +CONFIG_USB=y | |||
| +CONFIG_USB_XHCI_HCD=y | |||
| +CONFIG_USB_XHCI_DWC3=n | |||
| +CONFIG_USB_EHCI_HCD=y | |||
| +CONFIG_USB_EHCI_GENERIC=y | |||
| +CONFIG_USB_HOST_ETHER=y | |||
| +CONFIG_USB_ETHER_ASIX=y | |||
| +CONFIG_USB_ETHER_ASIX88179=y | |||
| +CONFIG_USB_ETHER_MCS7830=y | |||
| +CONFIG_USB_ETHER_RTL8152=y | |||
| +CONFIG_USB_ETHER_SMSC95XX=y | |||
| +CONFIG_USB_KEYBOARD=y | |||
| +CONFIG_SPL_TINY_MEMSET=y | |||
| +CONFIG_ERRNO_STR=y | |||
| +CONFIG_DM_VIDEO=y | |||
| +CONFIG_VIDEO_BPP16=y | |||
| +CONFIG_VIDEO_BPP32=y | |||
| +CONFIG_DISPLAY=y | |||
| +CONFIG_VIDEO_ROCKCHIP=y | |||
| +CONFIG_DISPLAY_ROCKCHIP_HDMI=y | |||
| +CONFIG_LOG=y | |||
| +CONFIG_LOG_CONSOLE=y | |||
| +CONFIG_SPL_LOG=y | |||
| +CONFIG_SPL_LOG_CONSOLE=y | |||
| +CONFIG_LOG_MAX_LEVEL=7 | |||
| +# CONFIG_USE_TINY_PRINTF is not set | |||
| @@ -3,6 +3,7 @@ | |||
| , fetchurl | |||
| , fetchpatch | |||
| , fetchFromGitHub | |||
| , fetchFromGitLab | |||
| , bc | |||
| , bison | |||
| , dtc | |||
| @@ -19,9 +20,12 @@ | |||
| let | |||
| defaultVersion = "2020.07"; | |||
| defaultSrc = fetchurl { | |||
| url = "ftp://ftp.denx.de/pub/u-boot/u-boot-${defaultVersion}.tar.bz2"; | |||
| sha256 = "0sjzy262x93aaqd6z24ziaq19xjjjk5f577ivf768vmvwsgbzxf1"; | |||
| defaultSrc = fetchFromGitLab { | |||
| domain = "gitlab.denx.de"; | |||
| owner = "u-boot"; | |||
| repo = "u-boot"; | |||
| sha256 = "11154cxycw81dnmxfl10n2mgyass18jhjpwygqp7w1vjk9hgi4lw"; | |||
| rev = "2f5fbb5b39f7b67044dda5c35e4a4b31685a3109"; | |||
| }; | |||
| buildUBoot = { | |||
| version ? null | |||
| @@ -40,7 +44,16 @@ let | |||
| src = if src == null then defaultSrc else src; | |||
| patches = extraPatches; | |||
| patches = [ | |||
| ./add-board-nanopi-m4v2.patch | |||
| ./enable-DT-overlays-support.patch | |||
| ./general-prioritize-sd.patch | |||
| ./rk3399-always-init-rkclk.patch | |||
| ./rk3399-enable-stable-mac.patch | |||
| ./rk3399-populate-child-node-of-syscon.patch | |||
| ./sdmmc-force-fifo-mode-in-spl.patch | |||
| ./u-boot-add-trust-ini.patch | |||
| ]; | |||
| postPatch = '' | |||
| patchShebangs tools | |||
| @@ -173,6 +186,17 @@ in { | |||
| ''; | |||
| }; | |||
| ubootNanoPiM4v2 = buildUBoot { | |||
| extraMakeFlags = [ "all" "u-boot.itb" ]; | |||
| defconfig = "nanopi-m4v2-rk3399_defconfig"; | |||
| extraMeta.platforms = ["aarch64-linux"]; | |||
| BL31="${armTrustedFirmwareRK3399}/bl31.elf"; | |||
| filesToInstall = [ "u-boot.itb" "idbloader.img"]; | |||
| extraConfig = '' | |||
| LOG y | |||
| ''; | |||
| }; | |||
| ubootNovena = buildUBoot { | |||
| defconfig = "novena_defconfig"; | |||
| extraMeta.platforms = ["armv7l-linux"]; | |||
| @@ -0,0 +1,13 @@ | |||
| diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig | |||
| index 1f3fa15..f559fa4 100644 | |||
| --- a/arch/arm/Kconfig | |||
| +++ b/arch/arm/Kconfig | |||
| @@ -1358,6 +1358,8 @@ config ARCH_ROCKCHIP | |||
| imply SYS_NS16550 | |||
| imply TPL_SYSRESET | |||
| imply USB_FUNCTION_FASTBOOT | |||
| + select OF_LIBFDT | |||
| + select OF_LIBFDT_OVERLAY | |||
| config TARGET_THUNDERX_88XX | |||
| bool "Support ThunderX 88xx" | |||
| @@ -0,0 +1,15 @@ | |||
| diff --git a/include/configs/rockchip-common.h b/include/configs/rockchip-common.h | |||
| index 68e1105a..906c22ed 100644 | |||
| --- a/include/configs/rockchip-common.h | |||
| +++ b/include/configs/rockchip-common.h | |||
| @@ -14,8 +14,8 @@ | |||
| /* First try to boot from SD (index 0), then eMMC (index 1) */ | |||
| #if CONFIG_IS_ENABLED(CMD_MMC) | |||
| #define BOOT_TARGET_MMC(func) \ | |||
| - func(MMC, mmc, 0) \ | |||
| - func(MMC, mmc, 1) | |||
| + func(MMC, mmc, 1) \ | |||
| + func(MMC, mmc, 0) | |||
| #else | |||
| #define BOOT_TARGET_MMC(func) | |||
| #endif | |||
| @@ -0,0 +1,54 @@ | |||
| This patch enables clk initialisation of rk3399 cpu in u-boot proper. | |||
| Normally it should only be initialised in SPL as it is "time consuming". | |||
| Doing so however leaves cpus clocked to low frequencies | |||
| for Rockchip's DDR/loader/trust with mainline u-boot scenario | |||
| which does not involve SPL phase. | |||
| Signed-off-by: Piotr Szczepanik <piter75@gmail.com> | |||
| diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c | |||
| index 865b80cc..fe546436 100644 | |||
| --- a/drivers/clk/rockchip/clk_rk3399.c | |||
| +++ b/drivers/clk/rockchip/clk_rk3399.c | |||
| @@ -47,12 +47,9 @@ struct pll_div { | |||
| .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\ | |||
| .postdiv1 = _postdiv1, .postdiv2 = _postdiv2}; | |||
| -#if defined(CONFIG_SPL_BUILD) | |||
| static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1); | |||
| static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2, 2); | |||
| -#else | |||
| static const struct pll_div ppll_init_cfg = PLL_DIVISORS(PPLL_HZ, 2, 2, 1); | |||
| -#endif | |||
| static const struct pll_div apll_l_1600_cfg = PLL_DIVISORS(1600 * MHz, 3, 1, 1); | |||
| static const struct pll_div apll_l_600_cfg = PLL_DIVISORS(600 * MHz, 1, 2, 1); | |||
| @@ -1071,7 +1068,6 @@ static struct clk_ops rk3399_clk_ops = { | |||
| #endif | |||
| }; | |||
| -#ifdef CONFIG_SPL_BUILD | |||
| static void rkclk_init(struct rockchip_cru *cru) | |||
| { | |||
| u32 aclk_div; | |||
| @@ -1149,11 +1145,9 @@ static void rkclk_init(struct rockchip_cru *cru) | |||
| hclk_div << HCLK_PERILP1_DIV_CON_SHIFT | | |||
| HCLK_PERILP1_PLL_SEL_GPLL << HCLK_PERILP1_PLL_SEL_SHIFT); | |||
| } | |||
| -#endif | |||
| static int rk3399_clk_probe(struct udevice *dev) | |||
| { | |||
| -#ifdef CONFIG_SPL_BUILD | |||
| struct rk3399_clk_priv *priv = dev_get_priv(dev); | |||
| #if CONFIG_IS_ENABLED(OF_PLATDATA) | |||
| @@ -1162,7 +1156,6 @@ static int rk3399_clk_probe(struct udevice *dev) | |||
| priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]); | |||
| #endif | |||
| rkclk_init(priv->cru); | |||
| -#endif | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| diff --git a/include/configs/rk3399_common.h b/include/configs/rk3399_common.h | |||
| index 126c3476..c6dccf4f 100644 | |||
| --- a/include/configs/rk3399_common.h | |||
| +++ b/include/configs/rk3399_common.h | |||
| @@ -19,6 +19,10 @@ | |||
| #define CONFIG_SYS_INIT_SP_ADDR 0x00300000 | |||
| #define CONFIG_SYS_LOAD_ADDR 0x00800800 | |||
| +#define CONFIG_MISC 1 | |||
| +#define CONFIG_MISC_INIT_R 1 | |||
| +#define CONFIG_ROCKCHIP_EFUSE 1 | |||
| + | |||
| #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_TPL_BOOTROM_SUPPORT) | |||
| #define CONFIG_SPL_STACK 0x00400000 | |||
| #define CONFIG_SPL_MAX_SIZE 0x40000 | |||
| @@ -0,0 +1,31 @@ | |||
| From 91cbd5f4fb25281319034c33b6e0c0f9b7d1e12b Mon Sep 17 00:00:00 2001 | |||
| Message-Id: <91cbd5f4fb25281319034c33b6e0c0f9b7d1e12b.1585676333.git.aditya@kobol.io> | |||
| In-Reply-To: <ef8793354dbb6bce3027a3492ffc1fee3588f41f.1585676333.git.aditya@kobol.io> | |||
| References: <ef8793354dbb6bce3027a3492ffc1fee3588f41f.1585676333.git.aditya@kobol.io> | |||
| From: Aditya Prayoga <aditya@kobol.io> | |||
| Date: Wed, 4 Mar 2020 22:10:31 +0700 | |||
| Subject: [PATCH 4/4] arm:rockchip:rk3399: Populate child node of syscon | |||
| U-Boot only populate first level of node. | |||
| Scan child node so device such as PHY can be initialized. | |||
| --- | |||
| arch/arm/mach-rockchip/rk3399/syscon_rk3399.c | 3 +++ | |||
| 1 file changed, 3 insertions(+) | |||
| diff --git a/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c b/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c | |||
| index 259ca44d68..81b04aa7f8 100644 | |||
| --- a/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c | |||
| +++ b/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c | |||
| @@ -21,6 +21,9 @@ U_BOOT_DRIVER(syscon_rk3399) = { | |||
| .name = "rk3399_syscon", | |||
| .id = UCLASS_SYSCON, | |||
| .of_match = rk3399_syscon_ids, | |||
| +#if !CONFIG_IS_ENABLED(OF_PLATDATA) | |||
| + .bind = dm_scan_fdt_dev, | |||
| +#endif | |||
| }; | |||
| #if CONFIG_IS_ENABLED(OF_PLATDATA) | |||
| -- | |||
| 2.17.1 | |||
| @@ -0,0 +1,22 @@ | |||
| From b0693aeb9ceab57ffc9d9f4ceca610bd82d5ca07 Mon Sep 17 00:00:00 2001 | |||
| From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= <ayufan@ayufan.eu> | |||
| Date: Mon, 21 May 2018 02:00:19 +0200 | |||
| Subject: [PATCH] ayufan: rock64: for SPL build always use fifo-mode | |||
| Change-Id: I9ac012ce4aaf03a151f7c5c818829d631efdd7ed | |||
| diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c | |||
| index fc0f0fad76..d1f26e41fe 100644 | |||
| --- a/drivers/mmc/rockchip_dw_mmc.c | |||
| +++ b/drivers/mmc/rockchip_dw_mmc.c | |||
| @@ -70,7 +70,11 @@ static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev) | |||
| if (priv->fifo_depth < 0) | |||
| return -EINVAL; | |||
| +#ifdef CONFIG_SPL_BUILD | |||
| + priv->fifo_mode = true; // always force fifo mode | |||
| +#else | |||
| priv->fifo_mode = dev_read_bool(dev, "fifo-mode"); | |||
| +#endif | |||
| /* | |||
| * 'clock-freq-min-max' is deprecated | |||
| @@ -0,0 +1,21 @@ | |||
| diff --git a/trust.ini b/trust.ini | |||
| new file mode 100644 | |||
| index 0000000..4af021a | |||
| --- /dev/null | |||
| +++ b/trust.ini | |||
| @@ -0,0 +1,15 @@ | |||
| +[VERSION] | |||
| +MAJOR=1 | |||
| +MINOR=0 | |||
| +[BL30_OPTION] | |||
| +SEC=0 | |||
| +[BL31_OPTION] | |||
| +SEC=1 | |||
| +PATH=bl31.elf | |||
| +ADDR=0x10000 | |||
| +[BL32_OPTION] | |||
| +SEC=0 | |||
| +[BL33_OPTION] | |||
| +SEC=0 | |||
| +[OUTPUT] | |||
| +PATH=trust.bin | |||
| @@ -15,4 +15,53 @@ buildLinux (args // rec { | |||
| url = "mirror://kernel/linux/kernel/v5.x/linux-${version}.tar.xz"; | |||
| sha256 = "0jy0yrrixzql9dlk9305w98pja2pm6ijrdbai326595pnk740n9y"; | |||
| }; | |||
| patches = [ | |||
| ./nanopi/0001-arm64-dts-rockchip-remove-capacity-dmips-rk3399.patch | |||
| ./nanopi/0010-arm64-dts-rockchip-add-cw2015-node-to-PBP.patch | |||
| ./nanopi/0011-fix-wonky-wifi-bt-on-PBP.patch | |||
| ./nanopi/0012-add-suspend-to-rk3399-PBP.patch | |||
| ./nanopi/0013-arm64-dts-rockchip-setup-USB-type-c-port-as-dual-dat.patch | |||
| ./nanopi/0015-add-dp-alt-mode-to-PBP.patch | |||
| ./nanopi/RK3399-enable_1.5_2.0_ghz_cpufreq_opp.patch | |||
| ./nanopi/add-board-nanopi-m4v2.patch | |||
| ./nanopi/add-csgpio-to-rockchip-spi.patch | |||
| ./nanopi/add-fusb30x-driver.patch | |||
| ./nanopi/add-maker-friendlyarm.patch | |||
| ./nanopi/board-nanopi-m4v2-dts-add-sound-card.patch | |||
| ./nanopi/board-nanopi-m4v2-dts-ethernet-tweak.patch | |||
| ./nanopi/fix-spi1-flash-speed.patch | |||
| ./nanopi/general-add-overlay-compilation-support.patch | |||
| ./nanopi/general-add-overlay-configfs.patch | |||
| ./nanopi/general-btsdio-ignore-uart-devs.patch | |||
| ./nanopi/general-emmc-hs400es-init-tweak.patch | |||
| ./nanopi/general-fix-es8316-kernel-panic.patch | |||
| ./nanopi/general-increasing_DMA_block_memory_allocation_to_2048.patch | |||
| ./nanopi/general-rockchip-overlays.patch | |||
| ./nanopi/general-rt5651-add-mclk.patch | |||
| ./nanopi/rk3399-add-sclk-i2sout-src-clock.patch | |||
| ./nanopi/rk3399-bump-voltages-for-low-opps.patch | |||
| ./nanopi/rk3399-sd-drive-level-8ma.patch | |||
| ./nanopi/rkvdec_nv15.patch | |||
| ./nanopi/unlock-temperature.patch | |||
| ./nanopi/zzz-0004-Add-DT-link-for-backward-compatibility.patch | |||
| ]; | |||
| extraConfig = '' | |||
| ARCH_ROCKCHIP y | |||
| CRYPTO_AEGIS128_SIMD n | |||
| FB_NVIDIA n | |||
| FB_NVIDIA_BACKLIGHT n | |||
| FB_NVIDIA_I2C n | |||
| FIREWIRE n | |||
| FPGA n | |||
| GREYBUS n | |||
| NET_VENDOR_NVIDIA n | |||
| RTC_DRV_RK808 y | |||
| SND n | |||
| SOUND n | |||
| STAGING y | |||
| STAGING_MEDIA y | |||
| ARCH_ROCKCHIP y | |||
| ''; | |||
| } // (args.argsOverride or {})) | |||
| @@ -0,0 +1,64 @@ | |||
| From 52505a426ae3a358708a2adc67d288b771df4192 Mon Sep 17 00:00:00 2001 | |||
| From: Dan Johansen <strit@manjaro.org> | |||
| Date: Mon, 16 Sep 2019 19:46:48 +0200 | |||
| Subject: [PATCH 1/3] arm64-dts-rockchip-remove-capacity-dmips-rk3399 | |||
| --- | |||
| arch/arm64/boot/dts/rockchip/rk3399.dtsi | 6 ------ | |||
| 1 file changed, 6 deletions(-) | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| index cede1ad81be2..e6e224205573 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| @@ -71,7 +71,6 @@ | |||
| compatible = "arm,cortex-a53"; | |||
| reg = <0x0 0x0>; | |||
| enable-method = "psci"; | |||
| - capacity-dmips-mhz = <485>; | |||
| clocks = <&cru ARMCLKL>; | |||
| #cooling-cells = <2>; /* min followed by max */ | |||
| dynamic-power-coefficient = <100>; | |||
| @@ -83,7 +82,6 @@ | |||
| compatible = "arm,cortex-a53"; | |||
| reg = <0x0 0x1>; | |||
| enable-method = "psci"; | |||
| - capacity-dmips-mhz = <485>; | |||
| clocks = <&cru ARMCLKL>; | |||
| #cooling-cells = <2>; /* min followed by max */ | |||
| dynamic-power-coefficient = <100>; | |||
| @@ -95,7 +93,6 @@ | |||
| compatible = "arm,cortex-a53"; | |||
| reg = <0x0 0x2>; | |||
| enable-method = "psci"; | |||
| - capacity-dmips-mhz = <485>; | |||
| clocks = <&cru ARMCLKL>; | |||
| #cooling-cells = <2>; /* min followed by max */ | |||
| dynamic-power-coefficient = <100>; | |||
| @@ -107,7 +104,6 @@ | |||
| compatible = "arm,cortex-a53"; | |||
| reg = <0x0 0x3>; | |||
| enable-method = "psci"; | |||
| - capacity-dmips-mhz = <485>; | |||
| clocks = <&cru ARMCLKL>; | |||
| #cooling-cells = <2>; /* min followed by max */ | |||
| dynamic-power-coefficient = <100>; | |||
| @@ -119,7 +115,6 @@ | |||
| compatible = "arm,cortex-a72"; | |||
| reg = <0x0 0x100>; | |||
| enable-method = "psci"; | |||
| - capacity-dmips-mhz = <1024>; | |||
| clocks = <&cru ARMCLKB>; | |||
| #cooling-cells = <2>; /* min followed by max */ | |||
| dynamic-power-coefficient = <436>; | |||
| @@ -131,7 +126,6 @@ | |||
| compatible = "arm,cortex-a72"; | |||
| reg = <0x0 0x101>; | |||
| enable-method = "psci"; | |||
| - capacity-dmips-mhz = <1024>; | |||
| clocks = <&cru ARMCLKB>; | |||
| #cooling-cells = <2>; /* min followed by max */ | |||
| dynamic-power-coefficient = <436>; | |||
| -- | |||
| 2.23.0 | |||
| @@ -0,0 +1,56 @@ | |||
| From bf967280def5ef5fe6e4cf1ce27196a9eb988cf5 Mon Sep 17 00:00:00 2001 | |||
| From: Dan Johansen <strit@manjaro.org> | |||
| Date: Sat, 30 May 2020 17:19:41 +0200 | |||
| Subject: [PATCH] arm64-dts-rockchip-add-cw2015-node-to-PBP | |||
| --- | |||
| .../boot/dts/rockchip/rk3399-pinebook-pro.dts | 26 +++++++++++++++++++ | |||
| 1 file changed, 26 insertions(+) | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| index c49982dfd8fc..e814917c013b 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| @@ -28,6 +28,13 @@ backlight: edp-backlight { | |||
| pwms = <&pwm0 0 740740 0>; | |||
| }; | |||
| + bat: battery { | |||
| + compatible = "simple-battery"; | |||
| + charge-full-design-microamp-hours = <9800000>; | |||
| + voltage-max-design-microvolt = <4350000>; | |||
| + voltage-min-design-microvolt = <3000000>; | |||
| + }; | |||
| + | |||
| edp_panel: edp-panel { | |||
| compatible = "boe,nv140fhmn49"; | |||
| backlight = <&backlight>; | |||
| @@ -741,6 +748,25 @@ usbc_dp: endpoint { | |||
| }; | |||
| }; | |||
| }; | |||
| + | |||
| + cw2015@62 { | |||
| + compatible = "cellwise,cw2015"; | |||
| + reg = <0x62>; | |||
| + cellwise,battery-profile = /bits/ 8 < | |||
| + 0x17 0x67 0x80 0x73 0x6E 0x6C 0x6B 0x63 | |||
| + 0x77 0x51 0x5C 0x58 0x50 0x4C 0x48 0x36 | |||
| + 0x15 0x0C 0x0C 0x19 0x5B 0x7D 0x6F 0x69 | |||
| + 0x69 0x5B 0x0C 0x29 0x20 0x40 0x52 0x59 | |||
| + 0x57 0x56 0x54 0x4F 0x3B 0x1F 0x7F 0x17 | |||
| + 0x06 0x1A 0x30 0x5A 0x85 0x93 0x96 0x2D | |||
| + 0x48 0x77 0x9C 0xB3 0x80 0x52 0x94 0xCB | |||
| + 0x2F 0x00 0x64 0xA5 0xB5 0x11 0xF0 0x11 | |||
| + >; | |||
| + cellwise,monitor-interval-ms = <5000>; | |||
| + monitored-battery = <&bat>; | |||
| + power-supplies = <&mains_charger>, <&fusb0>; | |||
| + }; | |||
| + | |||
| }; | |||
| &i2s1 { | |||
| -- | |||
| 2.26.2 | |||
| @@ -0,0 +1,150 @@ | |||
| From 0207f77ece3d07b964d5723c501adc3f3a5a3c6d Mon Sep 17 00:00:00 2001 | |||
| From: Dan Johansen <strit@manjaro.org> | |||
| Date: Mon, 1 Jun 2020 17:14:50 +0200 | |||
| Subject: [PATCH] fix wonky wifi/bt on PBP | |||
| --- | |||
| drivers/bluetooth/hci_bcm.c | 17 +++++++++++++++++ | |||
| drivers/bluetooth/hci_serdev.c | 2 ++ | |||
| drivers/mmc/core/pwrseq_simple.c | 19 ++++++++++++++++--- | |||
| drivers/tty/serdev/core.c | 11 +++++++++++ | |||
| include/linux/serdev.h | 1 + | |||
| 5 files changed, 47 insertions(+), 3 deletions(-) | |||
| diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c | |||
| index b236cb11c0dc..bfd37fb9eeb0 100644 | |||
| --- a/drivers/bluetooth/hci_bcm.c | |||
| +++ b/drivers/bluetooth/hci_bcm.c | |||
| @@ -1472,6 +1472,22 @@ static void bcm_serdev_remove(struct serdev_device *serdev) | |||
| hci_uart_unregister_device(&bcmdev->serdev_hu); | |||
| } | |||
| +static void bcm_serdev_shutdown(struct serdev_device *serdev) | |||
| +{ | |||
| + struct bcm_device *bcmdev = serdev_device_get_drvdata(serdev); | |||
| + | |||
| +/* | |||
| + if (test_bit(HCI_UART_REGISTERED, &bcmdev->hu->flags)) { | |||
| + hci_uart_unregister_device(&bcmdev->serdev_hu); | |||
| + } | |||
| +*/ | |||
| + dev_info(bcmdev->dev, "Cutting power to bluetooth module\n"); | |||
| + if (bcm_gpio_set_power(bcmdev, false)) { | |||
| + dev_err(bcmdev->dev, "Failed to power down\n"); | |||
| + } | |||
| + usleep_range(500000, 1000000); | |||
| +} | |||
| + | |||
| #ifdef CONFIG_OF | |||
| static struct bcm_device_data bcm4354_device_data = { | |||
| .no_early_set_baudrate = true, | |||
| @@ -1497,6 +1513,7 @@ MODULE_DEVICE_TABLE(of, bcm_bluetooth_of_match); | |||
| static struct serdev_device_driver bcm_serdev_driver = { | |||
| .probe = bcm_serdev_probe, | |||
| .remove = bcm_serdev_remove, | |||
| + .shutdown = bcm_serdev_shutdown, | |||
| .driver = { | |||
| .name = "hci_uart_bcm", | |||
| .of_match_table = of_match_ptr(bcm_bluetooth_of_match), | |||
| diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c | |||
| index 4652896d4990..043c585b34a7 100644 | |||
| --- a/drivers/bluetooth/hci_serdev.c | |||
| +++ b/drivers/bluetooth/hci_serdev.c | |||
| @@ -364,5 +364,7 @@ void hci_uart_unregister_device(struct hci_uart *hu) | |||
| hu->proto->close(hu); | |||
| serdev_device_close(hu->serdev); | |||
| + | |||
| +clear_bit(HCI_UART_REGISTERED, &hu->flags); | |||
| } | |||
| EXPORT_SYMBOL_GPL(hci_uart_unregister_device); | |||
| diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c | |||
| index ea4d3670560e..b52c3f5b4f13 100644 | |||
| --- a/drivers/mmc/core/pwrseq_simple.c | |||
| +++ b/drivers/mmc/core/pwrseq_simple.c | |||
| @@ -80,10 +80,8 @@ static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) | |||
| msleep(pwrseq->post_power_on_delay_ms); | |||
| } | |||
| -static void mmc_pwrseq_simple_power_off(struct mmc_host *host) | |||
| +static void __mmc_pwrseq_simple_power_off(struct mmc_pwrseq_simple *pwrseq) | |||
| { | |||
| - struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); | |||
| - | |||
| mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); | |||
| if (pwrseq->power_off_delay_us) | |||
| @@ -96,6 +94,12 @@ static void mmc_pwrseq_simple_power_off(struct mmc_host *host) | |||
| } | |||
| } | |||
| +static void mmc_pwrseq_simple_power_off(struct mmc_host *host) | |||
| +{ | |||
| + struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); | |||
| + __mmc_pwrseq_simple_power_off(pwrseq); | |||
| +} | |||
| + | |||
| static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { | |||
| .pre_power_on = mmc_pwrseq_simple_pre_power_on, | |||
| .post_power_on = mmc_pwrseq_simple_post_power_on, | |||
| @@ -151,9 +155,18 @@ static int mmc_pwrseq_simple_remove(struct platform_device *pdev) | |||
| return 0; | |||
| } | |||
| +static void mmc_pwrseq_simple_shutdown(struct platform_device *pdev) | |||
| +{ | |||
| + struct mmc_pwrseq_simple *pwrseq = platform_get_drvdata(pdev); | |||
| + | |||
| + dev_info(&pdev->dev, "Turning off mmc\n"); | |||
| + __mmc_pwrseq_simple_power_off(pwrseq); | |||
| +} | |||
| + | |||
| static struct platform_driver mmc_pwrseq_simple_driver = { | |||
| .probe = mmc_pwrseq_simple_probe, | |||
| .remove = mmc_pwrseq_simple_remove, | |||
| + .shutdown = mmc_pwrseq_simple_shutdown, | |||
| .driver = { | |||
| .name = "pwrseq_simple", | |||
| .of_match_table = mmc_pwrseq_simple_of_match, | |||
| diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c | |||
| index c5f0d936b003..54bcb38f0c05 100644 | |||
| --- a/drivers/tty/serdev/core.c | |||
| +++ b/drivers/tty/serdev/core.c | |||
| @@ -432,11 +432,22 @@ static int serdev_drv_remove(struct device *dev) | |||
| return 0; | |||
| } | |||
| +static void serdev_drv_shutdown(struct device *dev) | |||
| +{ | |||
| + const struct serdev_device_driver *sdrv; | |||
| + if (dev->driver) { | |||
| + sdrv = to_serdev_device_driver(dev->driver); | |||
| + if (sdrv->shutdown) | |||
| + sdrv->shutdown(to_serdev_device(dev)); | |||
| + } | |||
| +} | |||
| + | |||
| static struct bus_type serdev_bus_type = { | |||
| .name = "serial", | |||
| .match = serdev_device_match, | |||
| .probe = serdev_drv_probe, | |||
| .remove = serdev_drv_remove, | |||
| + .shutdown = serdev_drv_shutdown, | |||
| }; | |||
| /** | |||
| diff --git a/include/linux/serdev.h b/include/linux/serdev.h | |||
| index 9f14f9c12ec4..c3d5dccd6115 100644 | |||
| --- a/include/linux/serdev.h | |||
| +++ b/include/linux/serdev.h | |||
| @@ -63,6 +63,7 @@ struct serdev_device_driver { | |||
| struct device_driver driver; | |||
| int (*probe)(struct serdev_device *); | |||
| void (*remove)(struct serdev_device *); | |||
| + void (*shutdown)(struct serdev_device *); | |||
| }; | |||
| static inline struct serdev_device_driver *to_serdev_device_driver(struct device_driver *d) | |||
| -- | |||
| 2.26.2 | |||
| @@ -0,0 +1,1033 @@ | |||
| From c7b16d749d1746e3ab081f8550c3aba252969d26 Mon Sep 17 00:00:00 2001 | |||
| From: Dan Johansen <strit@manjaro.org> | |||
| Date: Mon, 1 Jun 2020 23:14:16 +0200 | |||
| Subject: [PATCH] add suspend to rk3399/PBP | |||
| --- | |||
| .../soc/rockchip/rockchip-pm-config.txt | 40 +++ | |||
| .../boot/dts/rockchip/rk3399-pinebook-pro.dts | 35 +++ | |||
| arch/arm64/boot/dts/rockchip/rk3399.dtsi | 24 ++ | |||
| drivers/firmware/Kconfig | 7 + | |||
| drivers/firmware/Makefile | 1 + | |||
| drivers/firmware/rockchip_sip.c | 263 ++++++++++++++++++ | |||
| drivers/regulator/core.c | 49 +++- | |||
| drivers/soc/rockchip/Kconfig | 6 + | |||
| drivers/soc/rockchip/Makefile | 1 + | |||
| drivers/soc/rockchip/rockchip_pm_config.c | 213 ++++++++++++++ | |||
| include/dt-bindings/suspend/rockchip-rk3399.h | 60 ++++ | |||
| include/linux/regulator/driver.h | 3 + | |||
| include/linux/rockchip/rockchip_sip.h | 149 ++++++++++ | |||
| 13 files changed, 847 insertions(+), 4 deletions(-) | |||
| create mode 100644 Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt | |||
| create mode 100644 drivers/firmware/rockchip_sip.c | |||
| create mode 100644 drivers/soc/rockchip/rockchip_pm_config.c | |||
| create mode 100644 include/dt-bindings/suspend/rockchip-rk3399.h | |||
| create mode 100644 include/linux/rockchip/rockchip_sip.h | |||
| diff --git a/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt b/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt | |||
| new file mode 100644 | |||
| index 000000000000..e15af33d3a9f | |||
| --- /dev/null | |||
| +++ b/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt | |||
| @@ -0,0 +1,40 @@ | |||
| +* the suspend mode config | |||
| + | |||
| +- compatible: "rockchip,pm-config" | |||
| + Compatibility with rk3399 | |||
| + | |||
| +- rockchip,sleep-mode-config : the sleep mode config, | |||
| + ARMOFF, OSC disabled ... | |||
| + | |||
| +- rockchip,wakeup-config: the wake up sourece enable. | |||
| + GPIO, USB, SD... | |||
| + | |||
| +- rockchip,pwm-regulator-config: the pwm regulator name. | |||
| + | |||
| +Example: | |||
| + rockchip_suspend: rockchip_suspend { | |||
| + compatible = "rockchip,pm-rk3399"; | |||
| + status = "okay"; | |||
| + rockchip,sleep-mode-config = < | |||
| + (0 | |||
| + | RKPM_SLP_ARMPD | |||
| + | RKPM_SLP_PERILPPD | |||
| + | RKPM_SLP_DDR_RET | |||
| + | RKPM_SLP_PLLPD | |||
| + | RKPM_SLP_OSC_DIS | |||
| + | RKPM_SLP_CENTER_PD | |||
| + | RKPM_SLP_AP_PWROFF | |||
| + ) | |||
| + >; | |||
| + rockchip,wakeup-config = < | |||
| + (0 | | |||
| + RKPM_GPIO_WKUP_EN | | |||
| + RKPM_PWM_WKUP_EN) | |||
| + >; | |||
| + rockchip,pwm-regulator-config = < | |||
| + (0 | | |||
| + PWM2_REGULATOR_EN | |||
| + ) | |||
| + >; | |||
| + }; | |||
| + | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| index c49982dfd8fc..9643cae1537a 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| @@ -19,9 +19,15 @@ / { | |||
| compatible = "pine64,pinebook-pro", "rockchip,rk3399"; | |||
| chosen { | |||
| + bootargs = "earlycon=uart8250,mmio32,0xff1a0000"; | |||
| stdout-path = "serial2:1500000n8"; | |||
| }; | |||
| + memory { | |||
| + device_type = "memory"; | |||
| + reg = <0x0 0x00200000 0x0 0xf7e00000>; | |||
| + }; | |||
| + | |||
| backlight: edp-backlight { | |||
| compatible = "pwm-backlight"; | |||
| power-supply = <&vcc_12v>; | |||
| @@ -125,6 +131,12 @@ sdio_pwrseq: sdio-pwrseq { | |||
| reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>; | |||
| }; | |||
| + /* first 128k(0xff8d0000~0xff8f0000) for ddr and ATF */ | |||
| + sram@ff8d0000 { | |||
| + compatible = "mmio-sram"; | |||
| + reg = <0x0 0xff8d0000 0x0 0x20000>; /* 128k */ | |||
| + }; | |||
| + | |||
| /* Audio components */ | |||
| es8316-sound { | |||
| compatible = "simple-audio-card"; | |||
| @@ -906,6 +918,29 @@ &pwm2 { | |||
| status = "okay"; | |||
| }; | |||
| +&rockchip_suspend { | |||
| + status = "okay"; | |||
| + rockchip,sleep-debug-en = <1>; | |||
| + rockchip,sleep-mode-config = < | |||
| + (0 | |||
| + | RKPM_SLP_ARMPD | |||
| + | RKPM_SLP_PERILPPD | |||
| + | RKPM_SLP_DDR_RET | |||
| + | RKPM_SLP_PLLPD | |||
| + | RKPM_SLP_CENTER_PD | |||
| + | RKPM_SLP_AP_PWROFF | |||
| + ) | |||
| + >; | |||
| + rockchip,pwm-regulator-config = < | |||
| + (0 | |||
| + | PWM2_REGULATOR_EN | |||
| + ) | |||
| + >; | |||
| + rockchip,power-ctrl = | |||
| + <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>, | |||
| + <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>; | |||
| +}; | |||
| + | |||
| &saradc { | |||
| vref-supply = <&vcca1v8_s3>; | |||
| status = "okay"; | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| index 1448f358ed0a..a91fc5c8b43b 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| @@ -9,6 +9,7 @@ | |||
| #include <dt-bindings/interrupt-controller/irq.h> | |||
| #include <dt-bindings/pinctrl/rockchip.h> | |||
| #include <dt-bindings/power/rk3399-power.h> | |||
| +#include <dt-bindings/suspend/rockchip-rk3399.h> | |||
| #include <dt-bindings/thermal/thermal.h> | |||
| / { | |||
| @@ -2652,4 +2653,27 @@ pcie_clkreqnb_cpm: pci-clkreqnb-cpm { | |||
| }; | |||
| }; | |||
| + | |||
| + rockchip_suspend: rockchip-suspend { | |||
| + compatible = "rockchip,pm-rk3399"; | |||
| + status = "disabled"; | |||
| + rockchip,sleep-debug-en = <0>; | |||
| + rockchip,virtual-poweroff = <0>; | |||
| + rockchip,sleep-mode-config = < | |||
| + (0 | |||
| + | RKPM_SLP_ARMPD | |||
| + | RKPM_SLP_PERILPPD | |||
| + | RKPM_SLP_DDR_RET | |||
| + | RKPM_SLP_PLLPD | |||
| + | RKPM_SLP_OSC_DIS | |||
| + | RKPM_SLP_CENTER_PD | |||
| + | RKPM_SLP_AP_PWROFF | |||
| + ) | |||
| + >; | |||
| + rockchip,wakeup-config = < | |||
| + (0 | |||
| + | RKPM_GPIO_WKUP_EN | |||
| + ) | |||
| + >; | |||
| + }; | |||
| }; | |||
| diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig | |||
| index 8007d4aa76dc..37f8039a5e27 100644 | |||
| --- a/drivers/firmware/Kconfig | |||
| +++ b/drivers/firmware/Kconfig | |||
| @@ -295,6 +295,13 @@ config TURRIS_MOX_RWTM | |||
| other manufacturing data and also utilize the Entropy Bit Generator | |||
| for hardware random number generation. | |||
| +config ROCKCHIP_SIP | |||
| + bool "Rockchip SIP interface" | |||
| + depends on ARM64 && ARM_PSCI_FW | |||
| + help | |||
| + Say Y here if you want to enable SIP callbacks for Rockchip platforms | |||
| + This option enables support for communicating with the ATF. | |||
| + | |||
| source "drivers/firmware/broadcom/Kconfig" | |||
| source "drivers/firmware/google/Kconfig" | |||
| source "drivers/firmware/efi/Kconfig" | |||
| diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile | |||
| index e9fb838af4df..575f45d55939 100644 | |||
| --- a/drivers/firmware/Makefile | |||
| +++ b/drivers/firmware/Makefile | |||
| @@ -29,6 +29,7 @@ obj-y += meson/ | |||
| obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ | |||
| obj-$(CONFIG_EFI) += efi/ | |||
| obj-$(CONFIG_UEFI_CPER) += efi/ | |||
| +obj-$(CONFIG_ROCKCHIP_SIP) += rockchip_sip.o | |||
| obj-y += imx/ | |||
| obj-y += tegra/ | |||
| obj-y += xilinx/ | |||
| diff --git a/drivers/firmware/rockchip_sip.c b/drivers/firmware/rockchip_sip.c | |||
| new file mode 100644 | |||
| index 000000000000..6844c5bad5b1 | |||
| --- /dev/null | |||
| +++ b/drivers/firmware/rockchip_sip.c | |||
| @@ -0,0 +1,263 @@ | |||
| +/* | |||
| + * 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. | |||
| + * | |||
| + * 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. | |||
| + * | |||
| + * Copyright (C) 2016, Fuzhou Rockchip Electronics Co., Ltd | |||
| + */ | |||
| + | |||
| +#include <linux/arm-smccc.h> | |||
| +#include <linux/io.h> | |||
| +#include <linux/rockchip/rockchip_sip.h> | |||
| +#include <asm/cputype.h> | |||
| +#include <asm/smp_plat.h> | |||
| +#include <uapi/linux/psci.h> | |||
| +#include <linux/ptrace.h> | |||
| + | |||
| +#ifdef CONFIG_64BIT | |||
| +#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name | |||
| +#else | |||
| +#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name | |||
| +#endif | |||
| + | |||
| +#define SIZE_PAGE(n) ((n) << 12) | |||
| + | |||
| +static struct arm_smccc_res __invoke_sip_fn_smc(unsigned long function_id, | |||
| + unsigned long arg0, | |||
| + unsigned long arg1, | |||
| + unsigned long arg2) | |||
| +{ | |||
| + struct arm_smccc_res res; | |||
| + | |||
| + arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); | |||
| + return res; | |||
| +} | |||
| + | |||
| +struct arm_smccc_res sip_smc_ddr_cfg(u32 arg0, u32 arg1, u32 arg2) | |||
| +{ | |||
| + return __invoke_sip_fn_smc(SIP_DDR_CFG, arg0, arg1, arg2); | |||
| +} | |||
| + | |||
| +struct arm_smccc_res sip_smc_get_atf_version(void) | |||
| +{ | |||
| + return __invoke_sip_fn_smc(SIP_ATF_VERSION, 0, 0, 0); | |||
| +} | |||
| + | |||
| +struct arm_smccc_res sip_smc_get_sip_version(void) | |||
| +{ | |||
| + return __invoke_sip_fn_smc(SIP_SIP_VERSION, 0, 0, 0); | |||
| +} | |||
| + | |||
| +int sip_smc_set_suspend_mode(u32 ctrl, u32 config1, u32 config2) | |||
| +{ | |||
| + struct arm_smccc_res res; | |||
| + | |||
| + res = __invoke_sip_fn_smc(SIP_SUSPEND_MODE, ctrl, config1, config2); | |||
| + return res.a0; | |||
| +} | |||
| + | |||
| +int sip_smc_virtual_poweroff(void) | |||
| +{ | |||
| + struct arm_smccc_res res; | |||
| + | |||
| + res = __invoke_sip_fn_smc(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), 0, 0, 0); | |||
| + return res.a0; | |||
| +} | |||
| + | |||
| +struct arm_smccc_res sip_smc_request_share_mem(u32 page_num, | |||
| + share_page_type_t page_type) | |||
| +{ | |||
| + struct arm_smccc_res res; | |||
| + unsigned long share_mem_phy; | |||
| + | |||
| + res = __invoke_sip_fn_smc(SIP_SHARE_MEM, page_num, page_type, 0); | |||
| + if (IS_SIP_ERROR(res.a0)) | |||
| + goto error; | |||
| + | |||
| + share_mem_phy = res.a1; | |||
| + res.a1 = (unsigned long)ioremap(share_mem_phy, SIZE_PAGE(page_num)); | |||
| + | |||
| +error: | |||
| + return res; | |||
| +} | |||
| + | |||
| +struct arm_smccc_res sip_smc_mcu_el3fiq(u32 arg0, u32 arg1, u32 arg2) | |||
| +{ | |||
| + return __invoke_sip_fn_smc(SIP_MCU_EL3FIQ_CFG, arg0, arg1, arg2); | |||
| +} | |||
| + | |||
| +/************************** fiq debugger **************************************/ | |||
| +#ifdef CONFIG_ARM64 | |||
| +#define SIP_UARTDBG_FN SIP_UARTDBG_CFG64 | |||
| +#else | |||
| +#define SIP_UARTDBG_FN SIP_UARTDBG_CFG | |||
| +#endif | |||
| + | |||
| +static int fiq_sip_enabled; | |||
| +static int fiq_target_cpu; | |||
| +static phys_addr_t ft_fiq_mem_phy; | |||
| +static void __iomem *ft_fiq_mem_base; | |||
| +static void (*sip_fiq_debugger_uart_irq_tf)(struct pt_regs _pt_regs, | |||
| + unsigned long cpu); | |||
| +int sip_fiq_debugger_is_enabled(void) | |||
| +{ | |||
| + return fiq_sip_enabled; | |||
| +} | |||
| + | |||
| +static struct pt_regs sip_fiq_debugger_get_pt_regs(void *reg_base, | |||
| + unsigned long sp_el1) | |||
| +{ | |||
| + struct pt_regs fiq_pt_regs; | |||
| + | |||
| +#ifdef CONFIG_ARM64 | |||
| + /* copy cpu context */ | |||
| + memcpy(&fiq_pt_regs, reg_base, 8 * 31); | |||
| + | |||
| + /* copy pstate */ | |||
| + memcpy(&fiq_pt_regs.pstate, reg_base + 0x110, 8); | |||
| + | |||
| + /* EL1 mode */ | |||
| + if (fiq_pt_regs.pstate & 0x10) | |||
| + memcpy(&fiq_pt_regs.sp, reg_base + 0xf8, 8); | |||
| + /* EL0 mode */ | |||
| + else | |||
| + fiq_pt_regs.sp = sp_el1; | |||
| + | |||
| + /* copy pc */ | |||
| + memcpy(&fiq_pt_regs.pc, reg_base + 0x118, 8); | |||
| +#else | |||
| + struct sm_nsec_ctx *nsec_ctx = reg_base; | |||
| + | |||
| + fiq_pt_regs.ARM_r0 = nsec_ctx->r0; | |||
| + fiq_pt_regs.ARM_r1 = nsec_ctx->r1; | |||
| + fiq_pt_regs.ARM_r2 = nsec_ctx->r2; | |||
| + fiq_pt_regs.ARM_r3 = nsec_ctx->r3; | |||
| + fiq_pt_regs.ARM_r4 = nsec_ctx->r4; | |||
| + fiq_pt_regs.ARM_r5 = nsec_ctx->r5; | |||
| + fiq_pt_regs.ARM_r6 = nsec_ctx->r6; | |||
| + fiq_pt_regs.ARM_r7 = nsec_ctx->r7; | |||
| + fiq_pt_regs.ARM_r8 = nsec_ctx->r8; | |||
| + fiq_pt_regs.ARM_r9 = nsec_ctx->r9; | |||
| + fiq_pt_regs.ARM_r10 = nsec_ctx->r10; | |||
| + fiq_pt_regs.ARM_fp = nsec_ctx->r11; | |||
| + fiq_pt_regs.ARM_ip = nsec_ctx->r12; | |||
| + fiq_pt_regs.ARM_sp = nsec_ctx->svc_sp; | |||
| + fiq_pt_regs.ARM_lr = nsec_ctx->svc_lr; | |||
| + fiq_pt_regs.ARM_pc = nsec_ctx->mon_lr; | |||
| + fiq_pt_regs.ARM_cpsr = nsec_ctx->mon_spsr; | |||
| +#endif | |||
| + | |||
| + return fiq_pt_regs; | |||
| +} | |||
| + | |||
| +static void sip_fiq_debugger_uart_irq_tf_cb(unsigned long sp_el1, | |||
| + unsigned long offset, | |||
| + unsigned long cpu) | |||
| +{ | |||
| + struct pt_regs fiq_pt_regs; | |||
| + char *cpu_context; | |||
| + | |||
| + /* calling fiq handler */ | |||
| + if (ft_fiq_mem_base) { | |||
| + cpu_context = (char *)ft_fiq_mem_base + offset; | |||
| + fiq_pt_regs = sip_fiq_debugger_get_pt_regs(cpu_context, sp_el1); | |||
| + sip_fiq_debugger_uart_irq_tf(fiq_pt_regs, cpu); | |||
| + } | |||
| + | |||
| + /* fiq handler done, return to EL3(then EL3 return to EL1 entry) */ | |||
| + __invoke_sip_fn_smc(SIP_UARTDBG_FN, 0, 0, UARTDBG_CFG_OSHDL_TO_OS); | |||
| +} | |||
| + | |||
| +int sip_fiq_debugger_uart_irq_tf_init(u32 irq_id, void *callback_fn) | |||
| +{ | |||
| + struct arm_smccc_res res; | |||
| + | |||
| + fiq_target_cpu = 0; | |||
| + | |||
| + /* init fiq debugger callback */ | |||
| + sip_fiq_debugger_uart_irq_tf = callback_fn; | |||
| + res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, irq_id, | |||
| + (unsigned long)sip_fiq_debugger_uart_irq_tf_cb, | |||
| + UARTDBG_CFG_INIT); | |||
| + if (IS_SIP_ERROR(res.a0)) { | |||
| + pr_err("%s error: %d\n", __func__, (int)res.a0); | |||
| + return res.a0; | |||
| + } | |||
| + | |||
| + /* share memory ioremap */ | |||
| + if (!ft_fiq_mem_base) { | |||
| + ft_fiq_mem_phy = res.a1; | |||
| + ft_fiq_mem_base = ioremap(ft_fiq_mem_phy, | |||
| + FIQ_UARTDBG_SHARE_MEM_SIZE); | |||
| + if (!ft_fiq_mem_base) { | |||
| + pr_err("%s: share memory ioremap failed\n", __func__); | |||
| + return -ENOMEM; | |||
| + } | |||
| + } | |||
| + | |||
| + fiq_sip_enabled = 1; | |||
| + | |||
| + return SIP_RET_SUCCESS; | |||
| +} | |||
| + | |||
| +int sip_fiq_debugger_switch_cpu(u32 cpu) | |||
| +{ | |||
| + struct arm_smccc_res res; | |||
| + | |||
| + fiq_target_cpu = cpu; | |||
| + res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, cpu_logical_map(cpu), | |||
| + 0, UARTDBG_CFG_OSHDL_CPUSW); | |||
| + return res.a0; | |||
| +} | |||
| + | |||
| +void sip_fiq_debugger_enable_debug(bool enable) | |||
| +{ | |||
| + unsigned long val; | |||
| + | |||
| + val = enable ? UARTDBG_CFG_OSHDL_DEBUG_ENABLE : | |||
| + UARTDBG_CFG_OSHDL_DEBUG_DISABLE; | |||
| + | |||
| + __invoke_sip_fn_smc(SIP_UARTDBG_FN, 0, 0, val); | |||
| +} | |||
| + | |||
| +int sip_fiq_debugger_set_print_port(u32 port_phyaddr, u32 baudrate) | |||
| +{ | |||
| + struct arm_smccc_res res; | |||
| + | |||
| + res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, port_phyaddr, baudrate, | |||
| + UARTDBG_CFG_PRINT_PORT); | |||
| + return res.a0; | |||
| +} | |||
| + | |||
| +int sip_fiq_debugger_request_share_memory(void) | |||
| +{ | |||
| + struct arm_smccc_res res; | |||
| + | |||
| + /* request page share memory */ | |||
| + res = sip_smc_request_share_mem(FIQ_UARTDBG_PAGE_NUMS, | |||
| + SHARE_PAGE_TYPE_UARTDBG); | |||
| + if (IS_SIP_ERROR(res.a0)) | |||
| + return res.a0; | |||
| + | |||
| + return SIP_RET_SUCCESS; | |||
| +} | |||
| + | |||
| +int sip_fiq_debugger_get_target_cpu(void) | |||
| +{ | |||
| + return fiq_target_cpu; | |||
| +} | |||
| + | |||
| +void sip_fiq_debugger_enable_fiq(bool enable, uint32_t tgt_cpu) | |||
| +{ | |||
| + u32 en; | |||
| + | |||
| + fiq_target_cpu = tgt_cpu; | |||
| + en = enable ? UARTDBG_CFG_FIQ_ENABEL : UARTDBG_CFG_FIQ_DISABEL; | |||
| + __invoke_sip_fn_smc(SIP_UARTDBG_FN, tgt_cpu, 0, en); | |||
| +} | |||
| + | |||
| diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c | |||
| index 7486f6e4e613..d46a267ea309 100644 | |||
| --- a/drivers/regulator/core.c | |||
| +++ b/drivers/regulator/core.c | |||
| @@ -5268,6 +5268,14 @@ void regulator_unregister(struct regulator_dev *rdev) | |||
| EXPORT_SYMBOL_GPL(regulator_unregister); | |||
| #ifdef CONFIG_SUSPEND | |||
| +static inline int can_enable(struct regulator_dev *rdev) { | |||
| + return rdev->ena_pin || rdev->desc->ops->enable; | |||
| +} | |||
| + | |||
| +static inline int can_disable(struct regulator_dev *rdev) { | |||
| + return rdev->ena_pin || rdev->desc->ops->disable; | |||
| +} | |||
| + | |||
| /** | |||
| * regulator_suspend - prepare regulators for system wide suspend | |||
| * @dev: ``&struct device`` pointer that is passed to _regulator_suspend() | |||
| @@ -5278,10 +5286,34 @@ static int regulator_suspend(struct device *dev) | |||
| { | |||
| struct regulator_dev *rdev = dev_to_rdev(dev); | |||
| suspend_state_t state = pm_suspend_target_state; | |||
| + struct regulator_state *rstate; | |||
| int ret; | |||
| regulator_lock(rdev); | |||
| ret = suspend_set_state(rdev, state); | |||
| + if (ret) { | |||
| + goto out; | |||
| + } | |||
| + | |||
| + rstate = regulator_get_suspend_state(rdev, state); | |||
| + if (rstate == NULL) | |||
| + goto out; | |||
| + | |||
| + if (rstate->enabled == ENABLE_IN_SUSPEND && can_enable(rdev)) { | |||
| + if (!rdev->desc->ops->set_suspend_enable) { | |||
| + rdev->resume_state = _regulator_is_enabled(rdev); | |||
| + rdev_info(rdev, "Entering suspend %d, enabling forcibly, was %s\n", state, rdev->resume_state ? "on" : "off"); | |||
| + ret = _regulator_do_enable(rdev); | |||
| + } | |||
| + } else if (rstate->enabled == DISABLE_IN_SUSPEND && can_disable(rdev)) { | |||
| + if (!rdev->desc->ops->set_suspend_disable) { | |||
| + rdev->resume_state = _regulator_is_enabled(rdev); | |||
| + rdev_info(rdev, "Entering suspend %d, disabling forcibly, was %s\n", state, rdev->resume_state ? "on" : "off"); | |||
| + ret = _regulator_do_disable(rdev); | |||
| + } | |||
| + } | |||
| +out: | |||
| + | |||
| regulator_unlock(rdev); | |||
| return ret; | |||
| @@ -5300,10 +5332,19 @@ static int regulator_resume(struct device *dev) | |||
| regulator_lock(rdev); | |||
| - if (rdev->desc->ops->resume && | |||
| - (rstate->enabled == ENABLE_IN_SUSPEND || | |||
| - rstate->enabled == DISABLE_IN_SUSPEND)) | |||
| - ret = rdev->desc->ops->resume(rdev); | |||
| + if (rstate->enabled == ENABLE_IN_SUSPEND || rstate->enabled == DISABLE_IN_SUSPEND) { | |||
| + if (rdev->desc->ops->resume) { | |||
| + ret = rdev->desc->ops->resume(rdev); | |||
| + } else if ((rstate->enabled == ENABLE_IN_SUSPEND && !rdev->desc->ops->set_suspend_enable) || | |||
| + (rstate->enabled == DISABLE_IN_SUSPEND && !rdev->desc->ops->set_suspend_disable)) { | |||
| + rdev_info(rdev, "Resuming, restoring state to %s\n", rdev->resume_state ? "on" : "off"); | |||
| + if (rdev->resume_state && can_enable(rdev)) { | |||
| + ret = _regulator_do_enable(rdev); | |||
| + } else if (!rdev->resume_state && can_disable(rdev)) { | |||
| + ret = _regulator_do_disable(rdev); | |||
| + } | |||
| + } | |||
| + } | |||
| regulator_unlock(rdev); | |||
| diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig | |||
| index b71b73bf5fc5..bfadbecd0df8 100644 | |||
| --- a/drivers/soc/rockchip/Kconfig | |||
| +++ b/drivers/soc/rockchip/Kconfig | |||
| @@ -26,4 +26,10 @@ config ROCKCHIP_PM_DOMAINS | |||
| If unsure, say N. | |||
| +config ROCKCHIP_SUSPEND_MODE | |||
| + bool "Rockchip suspend mode config" | |||
| + depends on ROCKCHIP_SIP | |||
| + help | |||
| + Say Y here if you want to set the suspend mode to the ATF. | |||
| + | |||
| endif | |||
| diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile | |||
| index afca0a4c4b72..a15c0a395a33 100644 | |||
| --- a/drivers/soc/rockchip/Makefile | |||
| +++ b/drivers/soc/rockchip/Makefile | |||
| @@ -4,3 +4,4 @@ | |||
| # | |||
| obj-$(CONFIG_ROCKCHIP_GRF) += grf.o | |||
| obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm_domains.o | |||
| +obj-$(CONFIG_ROCKCHIP_SUSPEND_MODE) += rockchip_pm_config.o | |||
| diff --git a/drivers/soc/rockchip/rockchip_pm_config.c b/drivers/soc/rockchip/rockchip_pm_config.c | |||
| new file mode 100644 | |||
| index 000000000000..4a3038f22639 | |||
| --- /dev/null | |||
| +++ b/drivers/soc/rockchip/rockchip_pm_config.c | |||
| @@ -0,0 +1,213 @@ | |||
| +/* | |||
| + * Rockchip Generic power configuration support. | |||
| + * | |||
| + * Copyright (c) 2017 ROCKCHIP, Co. Ltd. | |||
| + * | |||
| + * 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/arm-smccc.h> | |||
| +#include <linux/bitops.h> | |||
| +#include <linux/cpu.h> | |||
| +#include <linux/of_gpio.h> | |||
| +#include <linux/platform_device.h> | |||
| +#include <linux/regulator/machine.h> | |||
| +#include <linux/rockchip/rockchip_sip.h> | |||
| +#include <linux/suspend.h> | |||
| +#include <dt-bindings/input/input.h> | |||
| + | |||
| +#define PM_INVALID_GPIO 0xffff | |||
| + | |||
| +static const struct of_device_id pm_match_table[] = { | |||
| + { .compatible = "rockchip,pm-rk3399",}, | |||
| + { }, | |||
| +}; | |||
| + | |||
| +#define MAX_PWRKEY_NUMS 20 | |||
| +#define MAX_NUM_KEYS 60 | |||
| + | |||
| +struct rkxx_remote_key_table { | |||
| + int scancode; | |||
| + int keycode; | |||
| +}; | |||
| + | |||
| +static int parse_ir_pwrkeys(unsigned int *pwrkey, int size, int *nkey) | |||
| +{ | |||
| + struct device_node *node; | |||
| + struct device_node *child_node; | |||
| + struct rkxx_remote_key_table key_table[MAX_NUM_KEYS]; | |||
| + int i; | |||
| + int len = 0, nbuttons; | |||
| + int num = 0; | |||
| + u32 usercode, scancode; | |||
| + | |||
| + for_each_node_by_name(node, "pwm") { | |||
| + for_each_child_of_node(node, child_node) { | |||
| + if (of_property_read_u32(child_node, | |||
| + "rockchip,usercode", | |||
| + &usercode)) | |||
| + break; | |||
| + | |||
| + if (of_get_property(child_node, | |||
| + "rockchip,key_table", | |||
| + &len) == NULL || | |||
| + len <= 0) | |||
| + break; | |||
| + | |||
| + len = len < sizeof(key_table) ? len : sizeof(key_table); | |||
| + len /= sizeof(u32); | |||
| + if (of_property_read_u32_array(child_node, | |||
| + "rockchip,key_table", | |||
| + (u32 *)key_table, | |||
| + len)) | |||
| + break; | |||
| + | |||
| + nbuttons = len / 2; | |||
| + for (i = 0; i < nbuttons && num < size; ++i) { | |||
| + if (key_table[i].keycode == KEY_POWER) { | |||
| + scancode = key_table[i].scancode; | |||
| + pr_debug("usercode=%x, key=%x\n", | |||
| + usercode, scancode); | |||
| + pwrkey[num] = (usercode & 0xffff) << 16; | |||
| + pwrkey[num] |= (scancode & 0xff) << 8; | |||
| + ++num; | |||
| + } | |||
| + } | |||
| + } | |||
| + } | |||
| + | |||
| + *nkey = num; | |||
| + | |||
| + return num ? 0 : -1; | |||
| +} | |||
| + | |||
| +static void rockchip_pm_virt_pwroff_prepare(void) | |||
| +{ | |||
| + int error; | |||
| + int i, nkey; | |||
| + u32 power_key[MAX_PWRKEY_NUMS]; | |||
| + | |||
| + if ((parse_ir_pwrkeys(power_key, ARRAY_SIZE(power_key), &nkey))) { | |||
| + pr_err("Parse ir powerkey code failed!\n"); | |||
| + return; | |||
| + } | |||
| + | |||
| + for (i = 0; i < nkey; ++i) | |||
| + sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 1, power_key[i]); | |||
| + | |||
| + regulator_suspend_prepare(PM_SUSPEND_MEM); | |||
| + | |||
| + error = disable_nonboot_cpus(); | |||
| + if (error) { | |||
| + pr_err("Disable nonboot cpus failed!\n"); | |||
| + return; | |||
| + } | |||
| + | |||
| + sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 0, 1); | |||
| + sip_smc_virtual_poweroff(); | |||
| +} | |||
| + | |||
| +static int __init pm_config_init(struct platform_device *pdev) | |||
| +{ | |||
| + const struct of_device_id *match_id; | |||
| + struct device_node *node; | |||
| + u32 mode_config = 0; | |||
| + u32 wakeup_config = 0; | |||
| + u32 pwm_regulator_config = 0; | |||
| + int gpio_temp[10]; | |||
| + u32 sleep_debug_en = 0; | |||
| + u32 apios_suspend = 0; | |||
| + u32 virtual_poweroff_en = 0; | |||
| + enum of_gpio_flags flags; | |||
| + int i = 0; | |||
| + int length; | |||
| + | |||
| + match_id = of_match_node(pm_match_table, pdev->dev.of_node); | |||
| + if (!match_id) | |||
| + return -ENODEV; | |||
| + | |||
| + node = of_find_node_by_name(NULL, "rockchip-suspend"); | |||
| + | |||
| + if (IS_ERR_OR_NULL(node)) { | |||
| + dev_err(&pdev->dev, "%s dev node err\n", __func__); | |||
| + return -ENODEV; | |||
| + } | |||
| + | |||
| + if (of_property_read_u32_array(node, | |||
| + "rockchip,sleep-mode-config", | |||
| + &mode_config, 1)) | |||
| + dev_warn(&pdev->dev, "not set sleep mode config\n"); | |||
| + else | |||
| + sip_smc_set_suspend_mode(SUSPEND_MODE_CONFIG, mode_config, 0); | |||
| + | |||
| + if (of_property_read_u32_array(node, | |||
| + "rockchip,wakeup-config", | |||
| + &wakeup_config, 1)) | |||
| + dev_warn(&pdev->dev, "not set wakeup-config\n"); | |||
| + else | |||
| + sip_smc_set_suspend_mode(WKUP_SOURCE_CONFIG, wakeup_config, 0); | |||
| + | |||
| + if (of_property_read_u32_array(node, | |||
| + "rockchip,pwm-regulator-config", | |||
| + &pwm_regulator_config, 1)) | |||
| + dev_warn(&pdev->dev, "not set pwm-regulator-config\n"); | |||
| + else | |||
| + sip_smc_set_suspend_mode(PWM_REGULATOR_CONFIG, | |||
| + pwm_regulator_config, | |||
| + 0); | |||
| + | |||
| + length = of_gpio_named_count(node, "rockchip,power-ctrl"); | |||
| + | |||
| + if (length > 0 && length < 10) { | |||
| + for (i = 0; i < length; i++) { | |||
| + gpio_temp[i] = of_get_named_gpio_flags(node, | |||
| + "rockchip,power-ctrl", | |||
| + i, | |||
| + &flags); | |||
| + if (!gpio_is_valid(gpio_temp[i])) | |||
| + break; | |||
| + sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, | |||
| + i, | |||
| + gpio_temp[i]); | |||
| + } | |||
| + } | |||
| + sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, i, PM_INVALID_GPIO); | |||
| + | |||
| + if (!of_property_read_u32_array(node, | |||
| + "rockchip,sleep-debug-en", | |||
| + &sleep_debug_en, 1)) | |||
| + sip_smc_set_suspend_mode(SUSPEND_DEBUG_ENABLE, | |||
| + sleep_debug_en, | |||
| + 0); | |||
| + | |||
| + if (!of_property_read_u32_array(node, | |||
| + "rockchip,apios-suspend", | |||
| + &apios_suspend, 1)) | |||
| + sip_smc_set_suspend_mode(APIOS_SUSPEND_CONFIG, | |||
| + apios_suspend, | |||
| + 0); | |||
| + | |||
| + if (!of_property_read_u32_array(node, | |||
| + "rockchip,virtual-poweroff", | |||
| + &virtual_poweroff_en, 1) && | |||
| + virtual_poweroff_en) | |||
| + pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare; | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static struct platform_driver pm_driver = { | |||
| + .driver = { | |||
| + .name = "rockchip-pm", | |||
| + .of_match_table = pm_match_table, | |||
| + }, | |||
| +}; | |||
| + | |||
| +static int __init rockchip_pm_drv_register(void) | |||
| +{ | |||
| + return platform_driver_probe(&pm_driver, pm_config_init); | |||
| +} | |||
| +subsys_initcall(rockchip_pm_drv_register); | |||
| + | |||
| diff --git a/include/dt-bindings/suspend/rockchip-rk3399.h b/include/dt-bindings/suspend/rockchip-rk3399.h | |||
| new file mode 100644 | |||
| index 000000000000..de23670b43ea | |||
| --- /dev/null | |||
| +++ b/include/dt-bindings/suspend/rockchip-rk3399.h | |||
| @@ -0,0 +1,60 @@ | |||
| +/* | |||
| + * Header providing constants for Rockchip suspend bindings. | |||
| + * | |||
| + * Copyright (C) 2017, Fuzhou Rockchip Electronics Co., Ltd | |||
| + * Author: Tony.Xie | |||
| + * | |||
| + * 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. | |||
| + */ | |||
| + | |||
| +#ifndef __DT_BINDINGS_SUSPEND_ROCKCHIP_RK3399_H__ | |||
| +#define __DT_BINDINGS_SUSPEND_ROCKCHIP_RK3399_H__ | |||
| + | |||
| +/* the suspend mode */ | |||
| +#define RKPM_SLP_WFI (1 << 0) | |||
| +#define RKPM_SLP_ARMPD (1 << 1) | |||
| +#define RKPM_SLP_PERILPPD (1 << 2) | |||
| +#define RKPM_SLP_DDR_RET (1 << 3) | |||
| +#define RKPM_SLP_PLLPD (1 << 4) | |||
| +#define RKPM_SLP_OSC_DIS (1 << 5) | |||
| +#define RKPM_SLP_CENTER_PD (1 << 6) | |||
| +#define RKPM_SLP_AP_PWROFF (1 << 7) | |||
| + | |||
| +/* the wake up source */ | |||
| +#define RKPM_CLUSTER_L_WKUP_EN (1 << 0) | |||
| +#define RKPM_CLUSTER_B_WKUPB_EN (1 << 1) | |||
| +#define RKPM_GPIO_WKUP_EN (1 << 2) | |||
| +#define RKPM_SDIO_WKUP_EN (1 << 3) | |||
| +#define RKPM_SDMMC_WKUP_EN (1 << 4) | |||
| +#define RKPM_TIMER_WKUP_EN (1 << 6) | |||
| +#define RKPM_USB_WKUP_EN (1 << 7) | |||
| +#define RKPM_SFT_WKUP_EN (1 << 8) | |||
| +#define RKPM_WDT_M0_WKUP_EN (1 << 9) | |||
| +#define RKPM_TIME_OUT_WKUP_EN (1 << 10) | |||
| +#define RKPM_PWM_WKUP_EN (1 << 11) | |||
| +#define RKPM_PCIE_WKUP_EN (1 << 13) | |||
| + | |||
| +/* the pwm regulator */ | |||
| +#define PWM0_REGULATOR_EN (1 << 0) | |||
| +#define PWM1_REGULATOR_EN (1 << 1) | |||
| +#define PWM2_REGULATOR_EN (1 << 2) | |||
| +#define PWM3A_REGULATOR_EN (1 << 3) | |||
| +#define PWM3B_REGULATOR_EN (1 << 4) | |||
| + | |||
| +/* the APIO voltage domain */ | |||
| +#define RKPM_APIO0_SUSPEND (1 << 0) | |||
| +#define RKPM_APIO1_SUSPEND (1 << 1) | |||
| +#define RKPM_APIO2_SUSPEND (1 << 2) | |||
| +#define RKPM_APIO3_SUSPEND (1 << 3) | |||
| +#define RKPM_APIO4_SUSPEND (1 << 4) | |||
| +#define RKPM_APIO5_SUSPEND (1 << 5) | |||
| + | |||
| +#endif | |||
| diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h | |||
| index 29d920516e0b..549b68c58e68 100644 | |||
| --- a/include/linux/regulator/driver.h | |||
| +++ b/include/linux/regulator/driver.h | |||
| @@ -482,6 +482,9 @@ struct regulator_dev { | |||
| /* time when this regulator was disabled last time */ | |||
| unsigned long last_off_jiffy; | |||
| + | |||
| + /* state when resuming */ | |||
| + int resume_state; | |||
| }; | |||
| struct regulator_dev * | |||
| diff --git a/include/linux/rockchip/rockchip_sip.h b/include/linux/rockchip/rockchip_sip.h | |||
| new file mode 100644 | |||
| index 000000000000..9e8bbb2251c6 | |||
| --- /dev/null | |||
| +++ b/include/linux/rockchip/rockchip_sip.h | |||
| @@ -0,0 +1,149 @@ | |||
| +/* Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd | |||
| + * | |||
| + * This program is free software; you can redistribute it and/or modify | |||
| + * it under the terms of the GNU General Public License version 2 and | |||
| + * only version 2 as published by the Free Software Foundation. | |||
| + * | |||
| + * 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. | |||
| + */ | |||
| +#ifndef __ROCKCHIP_SIP_H | |||
| +#define __ROCKCHIP_SIP_H | |||
| + | |||
| +#include <linux/arm-smccc.h> | |||
| +#include <linux/io.h> | |||
| + | |||
| +/* SMC function IDs for SiP Service queries, compatible with kernel-3.10 */ | |||
| +#define SIP_ATF_VERSION 0x82000001 | |||
| +#define SIP_ACCESS_REG 0x82000002 | |||
| +#define SIP_SUSPEND_MODE 0x82000003 | |||
| +#define SIP_PENDING_CPUS 0x82000004 | |||
| +#define SIP_UARTDBG_CFG 0x82000005 | |||
| +#define SIP_UARTDBG_CFG64 0xc2000005 | |||
| +#define SIP_MCU_EL3FIQ_CFG 0x82000006 | |||
| +#define SIP_ACCESS_CHIP_STATE64 0xc2000006 | |||
| +#define SIP_SECURE_MEM_CONFIG 0x82000007 | |||
| +#define SIP_ACCESS_CHIP_EXTRA_STATE64 0xc2000007 | |||
| +#define SIP_DDR_CFG 0x82000008 | |||
| +#define SIP_SHARE_MEM 0x82000009 | |||
| +#define SIP_SIP_VERSION 0x8200000a | |||
| +#define SIP_REMOTECTL_CFG 0x8200000b | |||
| + | |||
| +/* Trust firmware version */ | |||
| +#define ATF_VER_MAJOR(ver) (((ver) >> 16) & 0xffff) | |||
| +#define ATF_VER_MINOR(ver) (((ver) >> 0) & 0xffff) | |||
| + | |||
| +/* SIP_ACCESS_REG: read or write */ | |||
| +#define SECURE_REG_RD 0x0 | |||
| +#define SECURE_REG_WR 0x1 | |||
| + | |||
| +/* Fiq debugger share memory: 8KB enough */ | |||
| +#define FIQ_UARTDBG_PAGE_NUMS 2 | |||
| +#define FIQ_UARTDBG_SHARE_MEM_SIZE ((FIQ_UARTDBG_PAGE_NUMS) * 4096) | |||
| + | |||
| +/* Error return code */ | |||
| +#define IS_SIP_ERROR(x) (!!(x)) | |||
| + | |||
| +#define SIP_RET_SUCCESS 0 | |||
| +#define SIP_RET_SMC_UNKNOWN -1 | |||
| +#define SIP_RET_NOT_SUPPORTED -2 | |||
| +#define SIP_RET_INVALID_PARAMS -3 | |||
| +#define SIP_RET_INVALID_ADDRESS -4 | |||
| +#define SIP_RET_DENIED -5 | |||
| + | |||
| +/* SIP_UARTDBG_CFG64 call types */ | |||
| +#define UARTDBG_CFG_INIT 0xf0 | |||
| +#define UARTDBG_CFG_OSHDL_TO_OS 0xf1 | |||
| +#define UARTDBG_CFG_OSHDL_CPUSW 0xf3 | |||
| +#define UARTDBG_CFG_OSHDL_DEBUG_ENABLE 0xf4 | |||
| +#define UARTDBG_CFG_OSHDL_DEBUG_DISABLE 0xf5 | |||
| +#define UARTDBG_CFG_PRINT_PORT 0xf7 | |||
| +#define UARTDBG_CFG_FIQ_ENABEL 0xf8 | |||
| +#define UARTDBG_CFG_FIQ_DISABEL 0xf9 | |||
| + | |||
| +/* SIP_SUSPEND_MODE32 call types */ | |||
| +#define SUSPEND_MODE_CONFIG 0x01 | |||
| +#define WKUP_SOURCE_CONFIG 0x02 | |||
| +#define PWM_REGULATOR_CONFIG 0x03 | |||
| +#define GPIO_POWER_CONFIG 0x04 | |||
| +#define SUSPEND_DEBUG_ENABLE 0x05 | |||
| +#define APIOS_SUSPEND_CONFIG 0x06 | |||
| +#define VIRTUAL_POWEROFF 0x07 | |||
| + | |||
| +/* SIP_REMOTECTL_CFG call types */ | |||
| +#define REMOTECTL_SET_IRQ 0xf0 | |||
| +#define REMOTECTL_SET_PWM_CH 0xf1 | |||
| +#define REMOTECTL_SET_PWRKEY 0xf2 | |||
| +#define REMOTECTL_GET_WAKEUP_STATE 0xf3 | |||
| +#define REMOTECTL_ENABLE 0xf4 | |||
| +/* wakeup state */ | |||
| +#define REMOTECTL_PWRKEY_WAKEUP 0xdeadbeaf | |||
| + | |||
| +/* Share mem page types */ | |||
| +typedef enum { | |||
| + SHARE_PAGE_TYPE_INVALID = 0, | |||
| + SHARE_PAGE_TYPE_UARTDBG, | |||
| + SHARE_PAGE_TYPE_MAX, | |||
| +} share_page_type_t; | |||
| + | |||
| +/* | |||
| + * Rules: struct arm_smccc_res contains result and data, details: | |||
| + * | |||
| + * a0: error code(0: success, !0: error); | |||
| + * a1~a3: data | |||
| + */ | |||
| +struct arm_smccc_res sip_smc_get_atf_version(void); | |||
| +struct arm_smccc_res sip_smc_get_sip_version(void); | |||
| +struct arm_smccc_res sip_smc_ddr_cfg(u32 arg0, u32 arg1, u32 arg2); | |||
| +struct arm_smccc_res sip_smc_request_share_mem(u32 page_num, | |||
| + share_page_type_t page_type); | |||
| +struct arm_smccc_res sip_smc_mcu_el3fiq(u32 arg0, u32 arg1, u32 arg2); | |||
| + | |||
| +int sip_smc_set_suspend_mode(u32 ctrl, u32 config1, u32 config2); | |||
| +int sip_smc_virtual_poweroff(void); | |||
| +/***************************fiq debugger **************************************/ | |||
| +void sip_fiq_debugger_enable_fiq(bool enable, uint32_t tgt_cpu); | |||
| +void sip_fiq_debugger_enable_debug(bool enable); | |||
| +int sip_fiq_debugger_uart_irq_tf_init(u32 irq_id, void *callback_fn); | |||
| +int sip_fiq_debugger_set_print_port(u32 port_phyaddr, u32 baudrate); | |||
| +int sip_fiq_debugger_request_share_memory(void); | |||
| +int sip_fiq_debugger_get_target_cpu(void); | |||
| +int sip_fiq_debugger_switch_cpu(u32 cpu); | |||
| +int sip_fiq_debugger_is_enabled(void); | |||
| + | |||
| +/* optee cpu_context */ | |||
| +struct sm_nsec_ctx { | |||
| + u32 usr_sp; | |||
| + u32 usr_lr; | |||
| + u32 irq_spsr; | |||
| + u32 irq_sp; | |||
| + u32 irq_lr; | |||
| + u32 svc_spsr; | |||
| + u32 svc_sp; | |||
| + u32 svc_lr; | |||
| + u32 abt_spsr; | |||
| + u32 abt_sp; | |||
| + u32 abt_lr; | |||
| + u32 und_spsr; | |||
| + u32 und_sp; | |||
| + u32 und_lr; | |||
| + u32 mon_lr; | |||
| + u32 mon_spsr; | |||
| + u32 r4; | |||
| + u32 r5; | |||
| + u32 r6; | |||
| + u32 r7; | |||
| + u32 r8; | |||
| + u32 r9; | |||
| + u32 r10; | |||
| + u32 r11; | |||
| + u32 r12; | |||
| + u32 r0; | |||
| + u32 r1; | |||
| + u32 r2; | |||
| + u32 r3; | |||
| +}; | |||
| + | |||
| +#endif | |||
| -- | |||
| 2.26.2 | |||
| @@ -0,0 +1,26 @@ | |||
| From 7d27663e4c2b112fbd035ea1b4d3ed074e98fbec Mon Sep 17 00:00:00 2001 | |||
| From: Dan Johansen <strit@manjaro.org> | |||
| Date: Sun, 7 Jun 2020 17:25:45 +0200 | |||
| Subject: [PATCH 1/2] arm64: dts: rockchip: setup USB type c port as dual data | |||
| role | |||
| --- | |||
| arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | 2 +- | |||
| 1 file changed, 1 insertion(+), 1 deletion(-) | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| index cb0245d2226d..3c1823944524 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| @@ -698,7 +698,7 @@ fusb0: fusb30x@22 { | |||
| connector { | |||
| compatible = "usb-c-connector"; | |||
| - data-role = "host"; | |||
| + data-role = "dual"; | |||
| label = "USB-C"; | |||
| op-sink-microwatt = <1000000>; | |||
| power-role = "dual"; | |||
| -- | |||
| 2.26.2 | |||
| @@ -0,0 +1,445 @@ | |||
| From a1bbffd1cbb883be7fe3da1d09c29d57cfbeb2da Mon Sep 17 00:00:00 2001 | |||
| From: Dan Johansen <strit@manjaro.org> | |||
| Date: Tue, 2 Jun 2020 20:20:29 +0200 | |||
| Subject: [PATCH] add-dp-alt-mode-to-PBP | |||
| --- | |||
| .../boot/dts/rockchip/rk3399-pinebook-pro.dts | 5 + | |||
| drivers/phy/rockchip/phy-rockchip-typec.c | 17 +++ | |||
| drivers/usb/typec/altmodes/displayport.c | 58 +++++++- | |||
| drivers/usb/typec/bus.c | 8 +- | |||
| drivers/usb/typec/tcpm/tcpm.c | 139 +++++++++++++++++- | |||
| 5 files changed, 221 insertions(+), 6 deletions(-) | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| index c49982dfd8fc..66cf08e8506f 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |||
| @@ -374,6 +374,7 @@ mains_charger: dc-charger { | |||
| &cdn_dp { | |||
| status = "okay"; | |||
| + extcon = <&fusb0>; | |||
| }; | |||
| &cpu_b0 { | |||
| @@ -708,6 +709,9 @@ connector { | |||
| <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>; | |||
| try-power-role = "sink"; | |||
| + extcon-cables = <1 2 5 6 9 10 12 44>; | |||
| + typec-altmodes = <0xff01 1 0x001c0000 1>; | |||
| + | |||
| ports { | |||
| #address-cells = <1>; | |||
| #size-cells = <0>; | |||
| @@ -958,6 +962,7 @@ spiflash: flash@0 { | |||
| }; | |||
| &tcphy0 { | |||
| + extcon = <&fusb0>; | |||
| status = "okay"; | |||
| }; | |||
| diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c | |||
| index 24563160197f..f5b497b4b97e 100644 | |||
| --- a/drivers/phy/rockchip/phy-rockchip-typec.c | |||
| +++ b/drivers/phy/rockchip/phy-rockchip-typec.c | |||
| @@ -40,6 +40,7 @@ | |||
| #include <linux/clk-provider.h> | |||
| #include <linux/delay.h> | |||
| #include <linux/extcon.h> | |||
| +#include <linux/extcon-provider.h> | |||
| #include <linux/io.h> | |||
| #include <linux/iopoll.h> | |||
| #include <linux/kernel.h> | |||
| @@ -1160,6 +1161,22 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) | |||
| dev_err(dev, "Invalid or missing extcon\n"); | |||
| return PTR_ERR(tcphy->extcon); | |||
| } | |||
| + } else { | |||
| + extcon_set_property_capability(tcphy->extcon, EXTCON_USB, | |||
| + EXTCON_PROP_USB_SS); | |||
| + extcon_set_property_capability(tcphy->extcon, EXTCON_USB_HOST, | |||
| + EXTCON_PROP_USB_SS); | |||
| + extcon_set_property_capability(tcphy->extcon, EXTCON_DISP_DP, | |||
| + EXTCON_PROP_USB_SS); | |||
| + extcon_set_property_capability(tcphy->extcon, EXTCON_USB, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY); | |||
| + extcon_set_property_capability(tcphy->extcon, EXTCON_USB_HOST, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY); | |||
| + extcon_set_property_capability(tcphy->extcon, EXTCON_DISP_DP, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY); | |||
| + extcon_sync(tcphy->extcon, EXTCON_USB); | |||
| + extcon_sync(tcphy->extcon, EXTCON_USB_HOST); | |||
| + extcon_sync(tcphy->extcon, EXTCON_DISP_DP); | |||
| } | |||
| pm_runtime_enable(dev); | |||
| diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c | |||
| index 0edfb89e04a8..40dd68c20159 100644 | |||
| --- a/drivers/usb/typec/altmodes/displayport.c | |||
| +++ b/drivers/usb/typec/altmodes/displayport.c | |||
| @@ -9,6 +9,8 @@ | |||
| */ | |||
| #include <linux/delay.h> | |||
| +#include <linux/extcon.h> | |||
| +#include <linux/extcon-provider.h> | |||
| #include <linux/mutex.h> | |||
| #include <linux/module.h> | |||
| #include <linux/usb/pd_vdo.h> | |||
| @@ -134,15 +136,53 @@ static int dp_altmode_status_update(struct dp_altmode *dp) | |||
| return ret; | |||
| } | |||
| +static void dp_altmode_update_extcon(struct dp_altmode *dp, bool disconnect) { | |||
| + const struct device *dev = &dp->port->dev; | |||
| + struct extcon_dev* edev = NULL; | |||
| + | |||
| + while (dev) { | |||
| + edev = extcon_find_edev_by_node(dev->of_node); | |||
| + if(!IS_ERR(edev)) { | |||
| + break; | |||
| + } | |||
| + dev = dev->parent; | |||
| + } | |||
| + | |||
| + if (IS_ERR_OR_NULL(edev)) { | |||
| + return; | |||
| + } | |||
| + | |||
| + if (disconnect || !dp->data.conf) { | |||
| + extcon_set_state_sync(edev, EXTCON_DISP_DP, false); | |||
| + } else { | |||
| + union extcon_property_value extcon_true = { .intval = true }; | |||
| + extcon_set_state(edev, EXTCON_DISP_DP, true); | |||
| + if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf) & DP_PIN_ASSIGN_MULTI_FUNC_MASK) { | |||
| + extcon_set_state_sync(edev, EXTCON_USB_HOST, true); | |||
| + extcon_set_property(edev, EXTCON_DISP_DP, EXTCON_PROP_USB_SS, | |||
| + extcon_true); | |||
| + } else { | |||
| + extcon_set_state_sync(edev, EXTCON_USB_HOST, false); | |||
| + } | |||
| + extcon_sync(edev, EXTCON_DISP_DP); | |||
| + extcon_set_state_sync(edev, EXTCON_USB, false); | |||
| + } | |||
| + | |||
| +} | |||
| + | |||
| static int dp_altmode_configured(struct dp_altmode *dp) | |||
| { | |||
| int ret; | |||
| sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration"); | |||
| - if (!dp->data.conf) | |||
| + if (!dp->data.conf) { | |||
| + dp_altmode_update_extcon(dp, true); | |||
| return typec_altmode_notify(dp->alt, TYPEC_STATE_USB, | |||
| &dp->data); | |||
| + } | |||
| + | |||
| + dp_altmode_update_extcon(dp, false); | |||
| ret = dp_altmode_notify(dp); | |||
| if (ret) | |||
| @@ -169,9 +209,11 @@ static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) | |||
| if (ret) { | |||
| if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) | |||
| dp_altmode_notify(dp); | |||
| - else | |||
| + else { | |||
| + dp_altmode_update_extcon(dp, true); | |||
| typec_altmode_notify(dp->alt, TYPEC_STATE_USB, | |||
| &dp->data); | |||
| + } | |||
| } | |||
| return ret; | |||
| @@ -210,6 +252,8 @@ static void dp_altmode_work(struct work_struct *work) | |||
| case DP_STATE_EXIT: | |||
| if (typec_altmode_exit(dp->alt)) | |||
| dev_err(&dp->alt->dev, "Exit Mode Failed!\n"); | |||
| + else | |||
| + dp_altmode_update_extcon(dp, true); | |||
| break; | |||
| default: | |||
| break; | |||
| @@ -520,8 +564,14 @@ int dp_altmode_probe(struct typec_altmode *alt) | |||
| if (!(DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) & | |||
| DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo)) && | |||
| !(DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) & | |||
| - DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo))) | |||
| - return -ENODEV; | |||
| + DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo))) { | |||
| + dev_err(&alt->dev, "No compatible pin configuration found:"\ | |||
| + "%04lx -> %04lx, %04lx <- %04lx", | |||
| + DP_CAP_DFP_D_PIN_ASSIGN(port->vdo), DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo), | |||
| + DP_CAP_UFP_D_PIN_ASSIGN(port->vdo), DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo)); | |||
| + return -ENODEV; | |||
| + } | |||
| + | |||
| ret = sysfs_create_group(&alt->dev.kobj, &dp_altmode_group); | |||
| if (ret) | |||
| diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c | |||
| index e8ddb81cb6df..cbc01d73739c 100644 | |||
| --- a/drivers/usb/typec/bus.c | |||
| +++ b/drivers/usb/typec/bus.c | |||
| @@ -154,8 +154,14 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit); | |||
| */ | |||
| void typec_altmode_attention(struct typec_altmode *adev, u32 vdo) | |||
| { | |||
| - struct typec_altmode *pdev = &to_altmode(adev)->partner->adev; | |||
| + struct typec_altmode *pdev; | |||
| + WARN_ONCE(!adev, "typec bus attention: adev is NULL!"); | |||
| + WARN_ONCE(!to_altmode(adev)->partner, "typec bus attention: partner is NULL!"); | |||
| + if(!adev || !to_altmode(adev)->partner) { | |||
| + return; | |||
| + } | |||
| + pdev = &to_altmode(adev)->partner->adev; | |||
| if (pdev->ops && pdev->ops->attention) | |||
| pdev->ops->attention(pdev, vdo); | |||
| } | |||
| diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c | |||
| index 82b19ebd7838..6f00b17afc15 100644 | |||
| --- a/drivers/usb/typec/tcpm/tcpm.c | |||
| +++ b/drivers/usb/typec/tcpm/tcpm.c | |||
| @@ -8,6 +8,7 @@ | |||
| #include <linux/completion.h> | |||
| #include <linux/debugfs.h> | |||
| #include <linux/device.h> | |||
| +#include <linux/extcon-provider.h> | |||
| #include <linux/jiffies.h> | |||
| #include <linux/kernel.h> | |||
| #include <linux/module.h> | |||
| @@ -322,6 +323,11 @@ struct tcpm_port { | |||
| /* port belongs to a self powered device */ | |||
| bool self_powered; | |||
| +#ifdef CONFIG_EXTCON | |||
| + struct extcon_dev *extcon; | |||
| + unsigned int *extcon_cables; | |||
| +#endif | |||
| + | |||
| #ifdef CONFIG_DEBUG_FS | |||
| struct dentry *dentry; | |||
| struct mutex logbuffer_lock; /* log buffer access lock */ | |||
| @@ -607,6 +613,35 @@ static void tcpm_debugfs_exit(const struct tcpm_port *port) { } | |||
| #endif | |||
| +static void tcpm_update_extcon_data(struct tcpm_port *port, bool attached) { | |||
| +#ifdef CONFIG_EXTCON | |||
| + unsigned int *capability = port->extcon_cables; | |||
| + if (port->data_role == TYPEC_HOST) { | |||
| + extcon_set_state(port->extcon, EXTCON_USB, false); | |||
| + extcon_set_state(port->extcon, EXTCON_USB_HOST, attached); | |||
| + } else { | |||
| + extcon_set_state(port->extcon, EXTCON_USB, true); | |||
| + extcon_set_state(port->extcon, EXTCON_USB_HOST, attached); | |||
| + } | |||
| + while (*capability != EXTCON_NONE) { | |||
| + if (attached) { | |||
| + union extcon_property_value val; | |||
| + val.intval = (port->polarity == TYPEC_POLARITY_CC2); | |||
| + extcon_set_property(port->extcon, *capability, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY, val); | |||
| + } else { | |||
| + extcon_set_state(port->extcon, *capability, false); | |||
| + } | |||
| + extcon_sync(port->extcon, *capability); | |||
| + capability++; | |||
| + } | |||
| + tcpm_log(port, "Extcon update (%s): %s, %s", | |||
| + attached ? "attached" : "detached", | |||
| + port->data_role == TYPEC_HOST ? "host" : "device", | |||
| + port->polarity == TYPEC_POLARITY_CC1 ? "normal" : "flipped"); | |||
| +#endif | |||
| +} | |||
| + | |||
| static int tcpm_pd_transmit(struct tcpm_port *port, | |||
| enum tcpm_transmit_type type, | |||
| const struct pd_message *msg) | |||
| @@ -834,6 +869,8 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached, | |||
| typec_set_data_role(port->typec_port, data); | |||
| typec_set_pwr_role(port->typec_port, role); | |||
| + tcpm_update_extcon_data(port, attached); | |||
| + | |||
| return 0; | |||
| } | |||
| @@ -1044,7 +1081,7 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload, | |||
| paltmode->mode = i; | |||
| paltmode->vdo = le32_to_cpu(payload[i]); | |||
| - tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x", | |||
| + tcpm_log(port, "Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x", | |||
| pmdata->altmodes, paltmode->svid, | |||
| paltmode->mode, paltmode->vdo); | |||
| @@ -1064,6 +1101,9 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port) | |||
| if (!altmode) | |||
| tcpm_log(port, "Failed to register partner SVID 0x%04x", | |||
| modep->altmode_desc[i].svid); | |||
| + else | |||
| + tcpm_log(port, "Registered altmode 0x%04x", modep->altmode_desc[i].svid); | |||
| + | |||
| port->partner_altmode[i] = altmode; | |||
| } | |||
| } | |||
| @@ -1167,9 +1207,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, | |||
| modep->svid_index++; | |||
| if (modep->svid_index < modep->nsvids) { | |||
| u16 svid = modep->svids[modep->svid_index]; | |||
| + tcpm_log(port, "More modes available, sending discover"); | |||
| response[0] = VDO(svid, 1, CMD_DISCOVER_MODES); | |||
| rlen = 1; | |||
| } else { | |||
| + tcpm_log(port, "Got all patner modes, registering"); | |||
| tcpm_register_partner_altmodes(port); | |||
| } | |||
| break; | |||
| @@ -2693,6 +2735,7 @@ static int tcpm_src_attach(struct tcpm_port *port) | |||
| static void tcpm_typec_disconnect(struct tcpm_port *port) | |||
| { | |||
| if (port->connected) { | |||
| + tcpm_update_extcon_data(port, false); | |||
| typec_unregister_partner(port->partner); | |||
| port->partner = NULL; | |||
| port->connected = false; | |||
| @@ -2750,6 +2793,8 @@ static void tcpm_detach(struct tcpm_port *port) | |||
| port->hard_reset_count = 0; | |||
| tcpm_reset_port(port); | |||
| + | |||
| + tcpm_update_extcon_data(port, false); | |||
| } | |||
| static void tcpm_src_detach(struct tcpm_port *port) | |||
| @@ -4424,6 +4469,64 @@ void tcpm_tcpc_reset(struct tcpm_port *port) | |||
| } | |||
| EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); | |||
| +unsigned int default_supported_cables[] = { | |||
| + EXTCON_NONE | |||
| +}; | |||
| + | |||
| +static int tcpm_fw_get_caps_late(struct tcpm_port *port, | |||
| + struct fwnode_handle *fwnode) | |||
| +{ | |||
| + int ret, i; | |||
| + ret = fwnode_property_count_u32(fwnode, "typec-altmodes"); | |||
| + if (ret > 0) { | |||
| + u32 *props; | |||
| + if (ret % 4) { | |||
| + dev_err(port->dev, "Length of typec altmode array must be divisible by 4"); | |||
| + return -EINVAL; | |||
| + } | |||
| + | |||
| + props = devm_kzalloc(port->dev, sizeof(u32) * ret, GFP_KERNEL); | |||
| + if (!props) { | |||
| + dev_err(port->dev, "Failed to allocate memory for altmode properties"); | |||
| + return -ENOMEM; | |||
| + } | |||
| + | |||
| + if(fwnode_property_read_u32_array(fwnode, "typec-altmodes", props, ret) < 0) { | |||
| + dev_err(port->dev, "Failed to read altmodes from port"); | |||
| + return -EINVAL; | |||
| + } | |||
| + | |||
| + i = 0; | |||
| + while (ret > 0 && i < ARRAY_SIZE(port->port_altmode)) { | |||
| + struct typec_altmode *alt; | |||
| + struct typec_altmode_desc alt_desc = { | |||
| + .svid = props[i * 4], | |||
| + .mode = props[i * 4 + 1], | |||
| + .vdo = props[i * 4 + 2], | |||
| + .roles = props[i * 4 + 3], | |||
| + }; | |||
| + | |||
| + | |||
| + tcpm_log(port, "Adding altmode SVID: 0x%04x, mode: %d, vdo: %u, role: %d", | |||
| + alt_desc.svid, alt_desc.mode, alt_desc.vdo, alt_desc.roles); | |||
| + alt = typec_port_register_altmode(port->typec_port, | |||
| + &alt_desc); | |||
| + if (IS_ERR(alt)) { | |||
| + tcpm_log(port, | |||
| + "%s: failed to register port alternate mode 0x%x", | |||
| + dev_name(port->dev), alt_desc.svid); | |||
| + break; | |||
| + } | |||
| + typec_altmode_set_drvdata(alt, port); | |||
| + alt->ops = &tcpm_altmode_ops; | |||
| + port->port_altmode[i] = alt; | |||
| + i++; | |||
| + ret -= 4; | |||
| + } | |||
| + } | |||
| + return 0; | |||
| +} | |||
| + | |||
| static int tcpm_fw_get_caps(struct tcpm_port *port, | |||
| struct fwnode_handle *fwnode) | |||
| { | |||
| @@ -4434,6 +4537,23 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, | |||
| if (!fwnode) | |||
| return -EINVAL; | |||
| +#ifdef CONFIG_EXTCON | |||
| + ret = fwnode_property_count_u32(fwnode, "extcon-cables"); | |||
| + if (ret > 0) { | |||
| + port->extcon_cables = devm_kzalloc(port->dev, sizeof(u32) * ret, GFP_KERNEL); | |||
| + if (!port->extcon_cables) { | |||
| + dev_err(port->dev, "Failed to allocate memory for extcon cable types. "\ | |||
| + "Using default tyes"); | |||
| + goto extcon_default; | |||
| + } | |||
| + fwnode_property_read_u32_array(fwnode, "extcon-cables", port->extcon_cables, ret); | |||
| + } else { | |||
| +extcon_default: | |||
| + dev_info(port->dev, "No cable types defined, using default cables"); | |||
| + port->extcon_cables = default_supported_cables; | |||
| + } | |||
| +#endif | |||
| + | |||
| /* USB data support is optional */ | |||
| ret = fwnode_property_read_string(fwnode, "data-role", &cap_str); | |||
| if (ret == 0) { | |||
| @@ -4766,6 +4886,17 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) | |||
| goto out_destroy_wq; | |||
| port->try_role = port->typec_caps.prefer_role; | |||
| +#ifdef CONFIG_EXTCON | |||
| + port->extcon = devm_extcon_dev_allocate(dev, port->extcon_cables); | |||
| + if (IS_ERR(port->extcon)) { | |||
| + dev_err(dev, "Failed to allocate extcon device: %ld", PTR_ERR(port->extcon)); | |||
| + goto out_destroy_wq; | |||
| + } | |||
| + if((err = devm_extcon_dev_register(dev, port->extcon))) { | |||
| + dev_err(dev, "Failed to register extcon device: %d", err); | |||
| + goto out_destroy_wq; | |||
| + } | |||
| +#endif | |||
| port->typec_caps.fwnode = tcpc->fwnode; | |||
| port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ | |||
| @@ -4793,6 +4924,12 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) | |||
| goto out_role_sw_put; | |||
| } | |||
| + err = tcpm_fw_get_caps_late(port, tcpc->fwnode); | |||
| + if (err < 0) { | |||
| + dev_err(dev, "Failed to get altmodes from fwnode"); | |||
| + goto out_destroy_wq; | |||
| + } | |||
| + | |||
| mutex_lock(&port->lock); | |||
| tcpm_init(port); | |||
| mutex_unlock(&port->lock); | |||
| -- | |||
| 2.26.2 | |||
| @@ -0,0 +1,26 @@ | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi | |||
| index d6f1095..30f353c 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi | |||
| @@ -33,6 +33,10 @@ | |||
| opp-hz = /bits/ 64 <1416000000>; | |||
| opp-microvolt = <1125000>; | |||
| }; | |||
| + opp06 { | |||
| + opp-hz = /bits/ 64 <1512000000>; | |||
| + opp-microvolt = <1200000>; | |||
| + }; | |||
| }; | |||
| cluster1_opp: opp-table1 { | |||
| @@ -72,6 +76,10 @@ | |||
| opp-hz = /bits/ 64 <1800000000>; | |||
| opp-microvolt = <1200000>; | |||
| }; | |||
| + opp08 { | |||
| + opp-hz = /bits/ 64 <2016000000>; | |||
| + opp-microvolt = <1300000>; | |||
| + }; | |||
| }; | |||
| gpu_opp_table: opp-table2 { | |||
| @@ -0,0 +1,84 @@ | |||
| diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile | |||
| index 948195b49..48dccb95e 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/Makefile | |||
| +++ b/arch/arm64/boot/dts/rockchip/Makefile | |||
| @@ -22,6 +22,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-khadas-edge-captain.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-khadas-edge-v.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopc-t4.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-m4.dtb | |||
| +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-m4v2.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-neo4.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-orangepi.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-pinebook-pro.dtb | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts | |||
| new file mode 100644 | |||
| index 000000000..60358ab8c | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts | |||
| @@ -0,0 +1,66 @@ | |||
| +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) | |||
| +/* | |||
| + * FriendlyElec NanoPi M4V2 board device tree source | |||
| + * | |||
| + * Copyright (c) 2018 FriendlyElec Computer Tech. Co., Ltd. | |||
| + * (http://www.friendlyarm.com) | |||
| + * | |||
| + * Copyright (c) 2018 Collabora Ltd. | |||
| + * Copyright (c) 2019 Arm Ltd. | |||
| + */ | |||
| + | |||
| +/dts-v1/; | |||
| +#include "rk3399-nanopi4.dtsi" | |||
| + | |||
| +/ { | |||
| + model = "FriendlyElec NanoPi M4 Ver2.0"; | |||
| + compatible = "friendlyarm,nanopi-m4", "rockchip,rk3399"; | |||
| + | |||
| + vdd_5v: vdd-5v { | |||
| + compatible = "regulator-fixed"; | |||
| + regulator-name = "vdd_5v"; | |||
| + regulator-always-on; | |||
| + regulator-boot-on; | |||
| + }; | |||
| + | |||
| + vcc5v0_core: vcc5v0-core { | |||
| + compatible = "regulator-fixed"; | |||
| + regulator-name = "vcc5v0_core"; | |||
| + regulator-always-on; | |||
| + regulator-boot-on; | |||
| + vin-supply = <&vdd_5v>; | |||
| + }; | |||
| + | |||
| + vcc5v0_usb1: vcc5v0-usb1 { | |||
| + compatible = "regulator-fixed"; | |||
| + regulator-name = "vcc5v0_usb1"; | |||
| + regulator-always-on; | |||
| + regulator-boot-on; | |||
| + vin-supply = <&vcc5v0_sys>; | |||
| + }; | |||
| + | |||
| + vcc5v0_usb2: vcc5v0-usb2 { | |||
| + compatible = "regulator-fixed"; | |||
| + regulator-name = "vcc5v0_usb2"; | |||
| + regulator-always-on; | |||
| + regulator-boot-on; | |||
| + vin-supply = <&vcc5v0_sys>; | |||
| + }; | |||
| +}; | |||
| + | |||
| +&vcc3v3_sys { | |||
| + vin-supply = <&vcc5v0_core>; | |||
| +}; | |||
| + | |||
| +&u2phy0_host { | |||
| + phy-supply = <&vcc5v0_usb1>; | |||
| +}; | |||
| + | |||
| +&u2phy1_host { | |||
| + phy-supply = <&vcc5v0_usb2>; | |||
| +}; | |||
| + | |||
| +&vbus_typec { | |||
| + regulator-always-on; | |||
| + vin-supply = <&vdd_5v>; | |||
| +}; | |||
| @@ -0,0 +1,83 @@ | |||
| diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c | |||
| index 2cc6d99..19e05ad 100644 | |||
| --- a/drivers/spi/spi-rockchip.c | |||
| +++ b/drivers/spi/spi-rockchip.c | |||
| @@ -6,6 +6,7 @@ | |||
| #include <linux/clk.h> | |||
| #include <linux/dmaengine.h> | |||
| +#include <linux/gpio.h> | |||
| #include <linux/interrupt.h> | |||
| #include <linux/module.h> | |||
| #include <linux/of.h> | |||
| @@ -185,6 +186,10 @@ struct rockchip_spi { | |||
| bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; | |||
| }; | |||
| +struct rockchip_spi_data { | |||
| + bool cs_gpio_requested; | |||
| +}; | |||
| + | |||
| static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable) | |||
| { | |||
| writel_relaxed((enable ? 1U : 0U), rs->regs + ROCKCHIP_SPI_SSIENR); | |||
| @@ -455,6 +460,50 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, | |||
| return 1; | |||
| } | |||
| +static int rockchip_spi_setup(struct spi_device *spi) | |||
| +{ | |||
| + int ret = 0; | |||
| + unsigned long flags = (spi->mode & SPI_CS_HIGH) ? | |||
| + GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH; | |||
| + struct rockchip_spi_data *data = spi_get_ctldata(spi); | |||
| + | |||
| + if (!gpio_is_valid(spi->cs_gpio)) | |||
| + return 0; | |||
| + | |||
| + if (!data) { | |||
| + data = kzalloc(sizeof(*data), GFP_KERNEL); | |||
| + if (!data) | |||
| + return -ENOMEM; | |||
| + spi_set_ctldata(spi, data); | |||
| + } | |||
| + | |||
| + if (!data->cs_gpio_requested) { | |||
| + ret = gpio_request_one(spi->cs_gpio, flags, | |||
| + dev_name(&spi->dev)); | |||
| + if (!ret) | |||
| + data->cs_gpio_requested = 1; | |||
| + } else | |||
| + ret = gpio_direction_output(spi->cs_gpio, flags); | |||
| + | |||
| + if (ret < 0) | |||
| + dev_err(&spi->dev, "Failed to setup cs gpio(%d): %d\n", | |||
| + spi->cs_gpio, ret); | |||
| + | |||
| + return ret; | |||
| +} | |||
| + | |||
| +static void rockchip_spi_cleanup(struct spi_device *spi) | |||
| +{ | |||
| + struct rockchip_spi_data *data = spi_get_ctldata(spi); | |||
| + | |||
| + if (data) { | |||
| + if (data->cs_gpio_requested) | |||
| + gpio_free(spi->cs_gpio); | |||
| + kfree(data); | |||
| + spi_set_ctldata(spi, NULL); | |||
| + } | |||
| +} | |||
| + | |||
| static void rockchip_spi_config(struct rockchip_spi *rs, | |||
| struct spi_device *spi, struct spi_transfer *xfer, | |||
| bool use_dma) | |||
| @@ -683,6 +732,8 @@ static int rockchip_spi_probe(struct platform_device *pdev) | |||
| ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT); | |||
| ctlr->set_cs = rockchip_spi_set_cs; | |||
| + ctlr->setup = rockchip_spi_setup; | |||
| + ctlr->cleanup = rockchip_spi_cleanup; | |||
| ctlr->transfer_one = rockchip_spi_transfer_one; | |||
| ctlr->max_transfer_size = rockchip_spi_max_transfer_size; | |||
| ctlr->handle_err = rockchip_spi_handle_err; | |||
| @@ -0,0 +1,4047 @@ | |||
| diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig | |||
| index 1abf76be2..7ad8b090c 100644 | |||
| --- a/drivers/staging/Kconfig | |||
| +++ b/drivers/staging/Kconfig | |||
| @@ -92,6 +92,8 @@ source "drivers/staging/fbtft/Kconfig" | |||
| source "drivers/staging/fsl-dpaa2/Kconfig" | |||
| +source "drivers/staging/fusb30x/Kconfig" | |||
| + | |||
| source "drivers/staging/wilc1000/Kconfig" | |||
| source "drivers/staging/most/Kconfig" | |||
| diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile | |||
| index ab0cbe881..2e308d901 100644 | |||
| --- a/drivers/staging/Makefile | |||
| +++ b/drivers/staging/Makefile | |||
| @@ -36,6 +36,7 @@ obj-$(CONFIG_UNISYSSPAR) += unisys/ | |||
| obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/ | |||
| obj-$(CONFIG_FB_TFT) += fbtft/ | |||
| obj-$(CONFIG_FSL_DPAA2) += fsl-dpaa2/ | |||
| +obj-$(CONFIG_FUSB_30X) += fusb30x/ | |||
| obj-$(CONFIG_WILC1000) += wilc1000/ | |||
| obj-$(CONFIG_MOST) += most/ | |||
| obj-$(CONFIG_KS7010) += ks7010/ | |||
| diff --git a/drivers/staging/fusb30x/Kconfig b/drivers/staging/fusb30x/Kconfig | |||
| new file mode 100644 | |||
| index 000000000..5bb75270f | |||
| --- /dev/null | |||
| +++ b/drivers/staging/fusb30x/Kconfig | |||
| @@ -0,0 +1,10 @@ | |||
| +config FUSB_30X | |||
| + tristate "Fairchild FUSB30X Type-C chip driver" | |||
| + depends on I2C | |||
| + help | |||
| + This is a driver for the Fairchild FUSB302 Type-C chip. It supports | |||
| + USB Type-C PD functionality controlled using I2C. | |||
| + | |||
| + This driver supports extcon reporting not yet implemented in the | |||
| + mainline FUSB302 driver. | |||
| + | |||
| diff --git a/drivers/staging/fusb30x/Makefile b/drivers/staging/fusb30x/Makefile | |||
| new file mode 100644 | |||
| index 000000000..1c8e35df3 | |||
| --- /dev/null | |||
| +++ b/drivers/staging/fusb30x/Makefile | |||
| @@ -0,0 +1,2 @@ | |||
| +# SPDX-License-Identifier: GPL-2.0 | |||
| +obj-$(CONFIG_FUSB_30X) += fusb30x.o | |||
| diff --git a/drivers/staging/fusb30x/fusb30x.c b/drivers/staging/fusb30x/fusb30x.c | |||
| new file mode 100644 | |||
| index 000000000..56d22648c | |||
| --- /dev/null | |||
| +++ b/drivers/staging/fusb30x/fusb30x.c | |||
| @@ -0,0 +1,3434 @@ | |||
| +/* | |||
| + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd | |||
| + * Author: Zain Wang <zain.wang@rock-chips.com> | |||
| + * | |||
| + * This program is free software; you can redistribute it and/or modify it | |||
| + * under the terms and conditions of the GNU General Public License, | |||
| + * version 2, as published by the Free Software Foundation. | |||
| + * | |||
| + * Some ideas are from chrome ec and fairchild GPL fusb302 driver. | |||
| + */ | |||
| + | |||
| +#include <linux/delay.h> | |||
| +#include <linux/extcon.h> | |||
| +#include <linux/extcon-provider.h> | |||
| +#include <linux/gpio.h> | |||
| +#include <linux/interrupt.h> | |||
| +#include <linux/module.h> | |||
| +#include <linux/of_gpio.h> | |||
| +#include <linux/regmap.h> | |||
| +#include <linux/power_supply.h> | |||
| + | |||
| +#include "fusb30x.h" | |||
| + | |||
| +#define FUSB302_MAX_REG (FUSB_REG_FIFO + 50) | |||
| +#define FUSB_MS_TO_NS(x) ((s64)x * 1000 * 1000) | |||
| + | |||
| +#define TYPEC_CC_VOLT_OPEN 0 | |||
| +#define TYPEC_CC_VOLT_RA 1 | |||
| +#define TYPEC_CC_VOLT_RD 2 | |||
| +#define TYPEC_CC_VOLT_RP 3 | |||
| + | |||
| +#define EVENT_CC BIT(0) | |||
| +#define EVENT_RX BIT(1) | |||
| +#define EVENT_TX BIT(2) | |||
| +#define EVENT_REC_RESET BIT(3) | |||
| +#define EVENT_WORK_CONTINUE BIT(5) | |||
| +#define EVENT_TIMER_MUX BIT(6) | |||
| +#define EVENT_TIMER_STATE BIT(7) | |||
| +#define EVENT_DELAY_CC BIT(8) | |||
| +#define FLAG_EVENT (EVENT_RX | EVENT_TIMER_MUX | \ | |||
| + EVENT_TIMER_STATE) | |||
| + | |||
| +#define PACKET_IS_CONTROL_MSG(header, type) \ | |||
| + (PD_HEADER_CNT(header) == 0 && \ | |||
| + PD_HEADER_TYPE(header) == type) | |||
| + | |||
| +#define PACKET_IS_DATA_MSG(header, type) \ | |||
| + (PD_HEADER_CNT(header) != 0 && \ | |||
| + PD_HEADER_TYPE(header) == type) | |||
| + | |||
| +/* | |||
| + * DisplayPort modes capabilities | |||
| + * ------------------------------- | |||
| + * <31:24> : Reserved (always 0). | |||
| + * <23:16> : UFP_D pin assignment supported | |||
| + * <15:8> : DFP_D pin assignment supported | |||
| + * <7> : USB 2.0 signaling (0b=yes, 1b=no) | |||
| + * <6> : Plug | Receptacle (0b == plug, 1b == receptacle) | |||
| + * <5:2> : xxx1: Supports DPv1.3, xx1x Supports USB Gen 2 signaling | |||
| + * Other bits are reserved. | |||
| + * <1:0> : signal direction ( 00b=rsv, 01b=sink, 10b=src 11b=both ) | |||
| + */ | |||
| +#define PD_DP_PIN_CAPS(x) ((((x) >> 6) & 0x1) ? (((x) >> 16) & 0x3f) \ | |||
| + : (((x) >> 8) & 0x3f)) | |||
| +#define PD_DP_SIGNAL_GEN2(x) (((x) >> 3) & 0x1) | |||
| + | |||
| +#define MODE_DP_PIN_A BIT(0) | |||
| +#define MODE_DP_PIN_B BIT(1) | |||
| +#define MODE_DP_PIN_C BIT(2) | |||
| +#define MODE_DP_PIN_D BIT(3) | |||
| +#define MODE_DP_PIN_E BIT(4) | |||
| +#define MODE_DP_PIN_F BIT(5) | |||
| + | |||
| +/* Pin configs B/D/F support multi-function */ | |||
| +#define MODE_DP_PIN_MF_MASK (MODE_DP_PIN_B | MODE_DP_PIN_D | MODE_DP_PIN_F) | |||
| +/* Pin configs A/B support BR2 signaling levels */ | |||
| +#define MODE_DP_PIN_BR2_MASK (MODE_DP_PIN_A | MODE_DP_PIN_B) | |||
| +/* Pin configs C/D/E/F support DP signaling levels */ | |||
| +#define MODE_DP_PIN_DP_MASK (MODE_DP_PIN_C | MODE_DP_PIN_D | \ | |||
| + MODE_DP_PIN_E | MODE_DP_PIN_F) | |||
| + | |||
| +/* | |||
| + * DisplayPort Status VDO | |||
| + * ---------------------- | |||
| + * <31:9> : Reserved (always 0). | |||
| + * <8> : IRQ_HPD : 1 == irq arrived since last message otherwise 0. | |||
| + * <7> : HPD state : 0 = HPD_LOW, 1 == HPD_HIGH | |||
| + * <6> : Exit DP Alt mode: 0 == maintain, 1 == exit | |||
| + * <5> : USB config : 0 == maintain current, 1 == switch to USB from DP | |||
| + * <4> : Multi-function preference : 0 == no pref, 1 == MF preferred. | |||
| + * <3> : enabled : is DPout on/off. | |||
| + * <2> : power low : 0 == normal or LPM disabled, 1 == DP disabled for LPM | |||
| + * <1:0> : connect status : 00b == no (DFP|UFP)_D is connected or disabled. | |||
| + * 01b == DFP_D connected, 10b == UFP_D connected, 11b == both. | |||
| + */ | |||
| +#define PD_VDO_DPSTS_HPD_IRQ(x) (((x) >> 8) & 0x1) | |||
| +#define PD_VDO_DPSTS_HPD_LVL(x) (((x) >> 7) & 0x1) | |||
| +#define PD_VDO_DPSTS_MF_PREF(x) (((x) >> 4) & 0x1) | |||
| + | |||
| +static u8 fusb30x_port_used; | |||
| +static struct fusb30x_chip *fusb30x_port_info[256]; | |||
| + | |||
| +static bool is_write_reg(struct device *dev, unsigned int reg) | |||
| +{ | |||
| + if (reg >= FUSB_REG_FIFO) | |||
| + return true; | |||
| + else | |||
| + return ((reg < (FUSB_REG_CONTROL4 + 1)) && (reg > 0x01)) ? | |||
| + true : false; | |||
| +} | |||
| + | |||
| +static bool is_volatile_reg(struct device *dev, unsigned int reg) | |||
| +{ | |||
| + if (reg > FUSB_REG_CONTROL4) | |||
| + return true; | |||
| + | |||
| + switch (reg) { | |||
| + case FUSB_REG_CONTROL0: | |||
| + case FUSB_REG_CONTROL1: | |||
| + case FUSB_REG_CONTROL3: | |||
| + case FUSB_REG_RESET: | |||
| + return true; | |||
| + } | |||
| + return false; | |||
| +} | |||
| + | |||
| +struct regmap_config fusb302_regmap_config = { | |||
| + .reg_bits = 8, | |||
| + .val_bits = 8, | |||
| + .writeable_reg = is_write_reg, | |||
| + .volatile_reg = is_volatile_reg, | |||
| + .max_register = FUSB302_MAX_REG, | |||
| + .cache_type = REGCACHE_RBTREE, | |||
| +}; | |||
| + | |||
| +static void dump_notify_info(struct fusb30x_chip *chip) | |||
| +{ | |||
| + dev_dbg(chip->dev, "port %d\n", chip->port_num); | |||
| + dev_dbg(chip->dev, "orientation %d\n", chip->notify.orientation); | |||
| + dev_dbg(chip->dev, "power_role %d\n", chip->notify.power_role); | |||
| + dev_dbg(chip->dev, "data_role %d\n", chip->notify.data_role); | |||
| + dev_dbg(chip->dev, "cc %d\n", chip->notify.is_cc_connected); | |||
| + dev_dbg(chip->dev, "pd %d\n", chip->notify.is_pd_connected); | |||
| + dev_dbg(chip->dev, "enter_mode %d\n", chip->notify.is_enter_mode); | |||
| + dev_dbg(chip->dev, "pin support %d\n", | |||
| + chip->notify.pin_assignment_support); | |||
| + dev_dbg(chip->dev, "pin def %d\n", chip->notify.pin_assignment_def); | |||
| + dev_dbg(chip->dev, "attention %d\n", chip->notify.attention); | |||
| +} | |||
| + | |||
| +static const unsigned int fusb302_cable[] = { | |||
| + EXTCON_USB, | |||
| + EXTCON_USB_HOST, | |||
| + EXTCON_CHG_USB_SDP, | |||
| + EXTCON_CHG_USB_CDP, | |||
| + EXTCON_CHG_USB_DCP, | |||
| + EXTCON_CHG_USB_SLOW, | |||
| + EXTCON_CHG_USB_FAST, | |||
| + EXTCON_DISP_DP, | |||
| + EXTCON_NONE, | |||
| +}; | |||
| + | |||
| +static void fusb_set_pos_power(struct fusb30x_chip *chip, int max_vol, | |||
| + int max_cur) | |||
| +{ | |||
| + int i; | |||
| + int pos_find; | |||
| + int tmp; | |||
| + | |||
| + pos_find = 0; | |||
| + for (i = PD_HEADER_CNT(chip->rec_head) - 1; i >= 0; i--) { | |||
| + switch (CAP_POWER_TYPE(chip->rec_load[i])) { | |||
| + case 0: | |||
| + /* Fixed Supply */ | |||
| + if ((CAP_FPDO_VOLTAGE(chip->rec_load[i]) * 50) <= | |||
| + max_vol && | |||
| + (CAP_FPDO_CURRENT(chip->rec_load[i]) * 10) <= | |||
| + max_cur) { | |||
| + chip->pos_power = i + 1; | |||
| + tmp = CAP_FPDO_VOLTAGE(chip->rec_load[i]); | |||
| + chip->pd_output_vol = tmp * 50; | |||
| + tmp = CAP_FPDO_CURRENT(chip->rec_load[i]); | |||
| + chip->pd_output_cur = tmp * 10; | |||
| + pos_find = 1; | |||
| + } | |||
| + break; | |||
| + case 1: | |||
| + /* Battery */ | |||
| + if ((CAP_VPDO_VOLTAGE(chip->rec_load[i]) * 50) <= | |||
| + max_vol && | |||
| + (CAP_VPDO_CURRENT(chip->rec_load[i]) * 10) <= | |||
| + max_cur) { | |||
| + chip->pos_power = i + 1; | |||
| + tmp = CAP_VPDO_VOLTAGE(chip->rec_load[i]); | |||
| + chip->pd_output_vol = tmp * 50; | |||
| + tmp = CAP_VPDO_CURRENT(chip->rec_load[i]); | |||
| + chip->pd_output_cur = tmp * 10; | |||
| + pos_find = 1; | |||
| + } | |||
| + break; | |||
| + default: | |||
| + /* not meet battery caps */ | |||
| + break; | |||
| + } | |||
| + if (pos_find) | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static int fusb302_set_pos_power_by_charge_ic(struct fusb30x_chip *chip) | |||
| +{ | |||
| + struct power_supply *psy = NULL; | |||
| + union power_supply_propval val; | |||
| + enum power_supply_property psp; | |||
| + int max_vol, max_cur; | |||
| + | |||
| + max_vol = 0; | |||
| + max_cur = 0; | |||
| + psy = power_supply_get_by_phandle(chip->dev->of_node, "charge-dev"); | |||
| + if (!psy || IS_ERR(psy)) | |||
| + return -1; | |||
| + | |||
| + psp = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX; | |||
| + if (power_supply_get_property(psy, psp, &val) == 0) | |||
| + max_vol = val.intval / 1000; | |||
| + | |||
| + psp = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; | |||
| + if (power_supply_get_property(psy, psp, &val) == 0) | |||
| + max_cur = val.intval / 1000; | |||
| + | |||
| + if (max_vol > 0 && max_cur > 0) | |||
| + fusb_set_pos_power(chip, max_vol, max_cur); | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| +void fusb_irq_disable(struct fusb30x_chip *chip) | |||
| +{ | |||
| + unsigned long irqflags = 0; | |||
| + | |||
| + spin_lock_irqsave(&chip->irq_lock, irqflags); | |||
| + if (chip->enable_irq) { | |||
| + disable_irq_nosync(chip->gpio_int_irq); | |||
| + chip->enable_irq = 0; | |||
| + } else { | |||
| + dev_warn(chip->dev, "irq have already disabled\n"); | |||
| + } | |||
| + spin_unlock_irqrestore(&chip->irq_lock, irqflags); | |||
| +} | |||
| + | |||
| +void fusb_irq_enable(struct fusb30x_chip *chip) | |||
| +{ | |||
| + unsigned long irqflags = 0; | |||
| + | |||
| + spin_lock_irqsave(&chip->irq_lock, irqflags); | |||
| + if (!chip->enable_irq) { | |||
| + enable_irq(chip->gpio_int_irq); | |||
| + chip->enable_irq = 1; | |||
| + } | |||
| + spin_unlock_irqrestore(&chip->irq_lock, irqflags); | |||
| +} | |||
| + | |||
| +static void platform_fusb_notify(struct fusb30x_chip *chip) | |||
| +{ | |||
| + bool plugged = false, flip = false, dfp = false, ufp = false, | |||
| + dp = false, usb_ss = false, hpd = false; | |||
| + union extcon_property_value property; | |||
| + | |||
| + if (chip->notify.is_cc_connected) | |||
| + chip->notify.orientation = | |||
| + (chip->cc_polarity == TYPEC_POLARITY_CC1) ? | |||
| + CC1 : CC2; | |||
| + | |||
| + /* avoid notify repeated */ | |||
| + if (memcmp(&chip->notify, &chip->notify_cmp, | |||
| + sizeof(struct notify_info))) { | |||
| + dump_notify_info(chip); | |||
| + chip->notify.attention = false; | |||
| + memcpy(&chip->notify_cmp, &chip->notify, | |||
| + sizeof(struct notify_info)); | |||
| + | |||
| + plugged = chip->notify.is_cc_connected || | |||
| + chip->notify.is_pd_connected; | |||
| + if (chip->notify.orientation != NONE) | |||
| + flip = (chip->notify.orientation == CC1) ? false : true; | |||
| + dp = chip->notify.is_enter_mode; | |||
| + | |||
| + if (dp) { | |||
| + dfp = true; | |||
| + usb_ss = (chip->notify.pin_assignment_def & | |||
| + MODE_DP_PIN_MF_MASK) ? true : false; | |||
| + hpd = GET_DP_STATUS_HPD(chip->notify.dp_status); | |||
| + } else if (chip->notify.data_role) { | |||
| + dfp = true; | |||
| + usb_ss = true; | |||
| + } else if (plugged) { | |||
| + ufp = true; | |||
| + usb_ss = true; | |||
| + } | |||
| + | |||
| + property.intval = flip; | |||
| + extcon_set_property(chip->extcon, EXTCON_USB, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY, property); | |||
| + extcon_set_property(chip->extcon, EXTCON_USB_HOST, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY, property); | |||
| + extcon_set_property(chip->extcon, EXTCON_DISP_DP, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY, property); | |||
| + | |||
| + property.intval = usb_ss; | |||
| + extcon_set_property(chip->extcon, EXTCON_USB, | |||
| + EXTCON_PROP_USB_SS, property); | |||
| + extcon_set_property(chip->extcon, EXTCON_USB_HOST, | |||
| + EXTCON_PROP_USB_SS, property); | |||
| + extcon_set_property(chip->extcon, EXTCON_DISP_DP, | |||
| + EXTCON_PROP_USB_SS, property); | |||
| + extcon_set_state(chip->extcon, EXTCON_USB, ufp); | |||
| + extcon_set_state(chip->extcon, EXTCON_USB_HOST, dfp); | |||
| + extcon_set_state(chip->extcon, EXTCON_DISP_DP, dp && hpd); | |||
| + extcon_sync(chip->extcon, EXTCON_USB); | |||
| + extcon_sync(chip->extcon, EXTCON_USB_HOST); | |||
| + extcon_sync(chip->extcon, EXTCON_DISP_DP); | |||
| + if (chip->notify.power_role == POWER_ROLE_SINK && | |||
| + chip->notify.is_pd_connected && | |||
| + chip->pd_output_vol > 0 && chip->pd_output_cur > 0) { | |||
| + extcon_set_state(chip->extcon, EXTCON_CHG_USB_FAST, true); | |||
| + property.intval = | |||
| + (chip->pd_output_cur << 15 | | |||
| + chip->pd_output_vol); | |||
| + extcon_set_property(chip->extcon, EXTCON_CHG_USB_FAST, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY, | |||
| + property); | |||
| + extcon_sync(chip->extcon, EXTCON_CHG_USB_FAST); | |||
| + } | |||
| + } | |||
| +} | |||
| + | |||
| +static bool platform_get_device_irq_state(struct fusb30x_chip *chip) | |||
| +{ | |||
| + return !gpiod_get_value(chip->gpio_int); | |||
| +} | |||
| + | |||
| +static void fusb_timer_start(struct hrtimer *timer, int ms) | |||
| +{ | |||
| + ktime_t ktime; | |||
| + | |||
| + ktime = ktime_set(0, FUSB_MS_TO_NS(ms)); | |||
| + hrtimer_start(timer, ktime, HRTIMER_MODE_REL); | |||
| +} | |||
| + | |||
| +static void platform_set_vbus_lvl_enable(struct fusb30x_chip *chip, int vbus_5v, | |||
| + int vbus_other) | |||
| +{ | |||
| + bool gpio_vbus_value = false; | |||
| + | |||
| + gpio_vbus_value = gpiod_get_value(chip->gpio_vbus_5v); | |||
| + if (chip->gpio_vbus_5v) { | |||
| + gpiod_set_raw_value(chip->gpio_vbus_5v, vbus_5v); | |||
| + } | |||
| + | |||
| + if (chip->gpio_vbus_other) | |||
| + gpiod_set_raw_value(chip->gpio_vbus_5v, vbus_other); | |||
| + | |||
| + if (chip->gpio_discharge && !vbus_5v && gpio_vbus_value) { | |||
| + gpiod_set_value(chip->gpio_discharge, 1); | |||
| + msleep(20); | |||
| + gpiod_set_value(chip->gpio_discharge, 0); | |||
| + } | |||
| +} | |||
| + | |||
| +static void set_state(struct fusb30x_chip *chip, enum connection_state state) | |||
| +{ | |||
| + dev_dbg(chip->dev, "port %d, state %d\n", chip->port_num, state); | |||
| + if (!state) | |||
| + dev_info(chip->dev, "PD disabled\n"); | |||
| + chip->conn_state = state; | |||
| + chip->sub_state = 0; | |||
| + chip->val_tmp = 0; | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| +} | |||
| + | |||
| +static int tcpm_get_message(struct fusb30x_chip *chip) | |||
| +{ | |||
| + u8 buf[32]; | |||
| + int len; | |||
| + | |||
| + do { | |||
| + regmap_raw_read(chip->regmap, FUSB_REG_FIFO, buf, 3); | |||
| + chip->rec_head = (buf[1] & 0xff) | ((buf[2] << 8) & 0xff00); | |||
| + | |||
| + len = PD_HEADER_CNT(chip->rec_head) << 2; | |||
| + regmap_raw_read(chip->regmap, FUSB_REG_FIFO, buf, len + 4); | |||
| + /* ignore good_crc message */ | |||
| + } while (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_GOODCRC)); | |||
| + | |||
| + memcpy(chip->rec_load, buf, len); | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static void fusb302_flush_rx_fifo(struct fusb30x_chip *chip) | |||
| +{ | |||
| + regmap_write(chip->regmap, FUSB_REG_CONTROL1, CONTROL1_RX_FLUSH); | |||
| +} | |||
| + | |||
| +static int tcpm_get_cc(struct fusb30x_chip *chip, int *CC1, int *CC2) | |||
| +{ | |||
| + u32 val; | |||
| + int *CC_MEASURE; | |||
| + u32 store; | |||
| + | |||
| + *CC1 = TYPEC_CC_VOLT_OPEN; | |||
| + *CC2 = TYPEC_CC_VOLT_OPEN; | |||
| + | |||
| + if (chip->cc_state & CC_STATE_TOGSS_CC1) | |||
| + CC_MEASURE = CC1; | |||
| + else | |||
| + CC_MEASURE = CC2; | |||
| + | |||
| + if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) { | |||
| + regmap_read(chip->regmap, FUSB_REG_SWITCHES0, &store); | |||
| + /* measure cc1 first */ | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, | |||
| + SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 | | |||
| + SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 | | |||
| + SWITCHES0_PDWN1 | SWITCHES0_PDWN2, | |||
| + SWITCHES0_PDWN1 | SWITCHES0_PDWN2 | | |||
| + SWITCHES0_MEAS_CC1); | |||
| + usleep_range(250, 300); | |||
| + | |||
| + regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); | |||
| + val &= STATUS0_BC_LVL; | |||
| + *CC1 = val ? TYPEC_CC_VOLT_RP : TYPEC_CC_VOLT_OPEN; | |||
| + | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, | |||
| + SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 | | |||
| + SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 | | |||
| + SWITCHES0_PDWN1 | SWITCHES0_PDWN2, | |||
| + SWITCHES0_PDWN1 | SWITCHES0_PDWN2 | | |||
| + SWITCHES0_MEAS_CC2); | |||
| + usleep_range(250, 300); | |||
| + | |||
| + regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); | |||
| + val &= STATUS0_BC_LVL; | |||
| + *CC2 = val ? TYPEC_CC_VOLT_RP : TYPEC_CC_VOLT_OPEN; | |||
| + | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, | |||
| + SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2, | |||
| + store); | |||
| + } else { | |||
| + regmap_read(chip->regmap, FUSB_REG_SWITCHES0, &store); | |||
| + val = store; | |||
| + val &= ~(SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 | | |||
| + SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2); | |||
| + if (chip->cc_state & CC_STATE_TOGSS_CC1) { | |||
| + val |= SWITCHES0_MEAS_CC1 | SWITCHES0_PU_EN1; | |||
| + } else { | |||
| + val |= SWITCHES0_MEAS_CC2 | SWITCHES0_PU_EN2; | |||
| + } | |||
| + regmap_write(chip->regmap, FUSB_REG_SWITCHES0, val); | |||
| + | |||
| + regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_high); | |||
| + usleep_range(250, 300); | |||
| + | |||
| + regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); | |||
| + if (val & STATUS0_COMP) { | |||
| + int retry = 3; | |||
| + int comp_times = 0; | |||
| + | |||
| + while (retry--) { | |||
| + regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_high); | |||
| + usleep_range(250, 300); | |||
| + regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); | |||
| + if (val & STATUS0_COMP) { | |||
| + comp_times++; | |||
| + if (comp_times == 3) { | |||
| + *CC_MEASURE = TYPEC_CC_VOLT_OPEN; | |||
| + regmap_write(chip->regmap, FUSB_REG_SWITCHES0, store); | |||
| + } | |||
| + } | |||
| + } | |||
| + } else { | |||
| + regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_low); | |||
| + regmap_read(chip->regmap, FUSB_REG_MEASURE, &val); | |||
| + usleep_range(250, 300); | |||
| + | |||
| + regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); | |||
| + | |||
| + if (val & STATUS0_COMP) | |||
| + *CC_MEASURE = TYPEC_CC_VOLT_RD; | |||
| + else | |||
| + *CC_MEASURE = TYPEC_CC_VOLT_RA; | |||
| + } | |||
| + regmap_write(chip->regmap, FUSB_REG_SWITCHES0, store); | |||
| + regmap_write(chip->regmap, FUSB_REG_MEASURE, | |||
| + chip->cc_meas_high); | |||
| + } | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static void tcpm_set_cc_pull_mode(struct fusb30x_chip *chip, enum CC_MODE mode) | |||
| +{ | |||
| + u8 val; | |||
| + | |||
| + switch (mode) { | |||
| + case CC_PULL_UP: | |||
| + if (chip->cc_polarity == TYPEC_POLARITY_CC1) | |||
| + val = SWITCHES0_PU_EN1; | |||
| + else | |||
| + val = SWITCHES0_PU_EN2; | |||
| + break; | |||
| + case CC_PULL_DOWN: | |||
| + val = SWITCHES0_PDWN1 | SWITCHES0_PDWN2; | |||
| + break; | |||
| + default: | |||
| + val = 0; | |||
| + break; | |||
| + } | |||
| + | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, | |||
| + SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 | | |||
| + SWITCHES0_PDWN1 | SWITCHES0_PDWN2, | |||
| + val); | |||
| + | |||
| + if (chip->cc_meas_high && mode == CC_PULL_UP) | |||
| + regmap_write(chip->regmap, FUSB_REG_MEASURE, | |||
| + chip->cc_meas_high); | |||
| +} | |||
| + | |||
| +static int tcpm_set_cc(struct fusb30x_chip *chip, enum role_mode mode) | |||
| +{ | |||
| + switch (mode) { | |||
| + case ROLE_MODE_DFP: | |||
| + tcpm_set_cc_pull_mode(chip, CC_PULL_UP); | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, | |||
| + CONTROL2_MODE | CONTROL2_TOG_RD_ONLY, | |||
| + CONTROL2_MODE_DFP | CONTROL2_TOG_RD_ONLY); | |||
| + break; | |||
| + case ROLE_MODE_UFP: | |||
| + tcpm_set_cc_pull_mode(chip, CC_PULL_UP); | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, | |||
| + CONTROL2_MODE | CONTROL2_TOG_RD_ONLY, | |||
| + CONTROL2_MODE_UFP); | |||
| + break; | |||
| + case ROLE_MODE_DRP: | |||
| + tcpm_set_cc_pull_mode(chip, CC_PULL_NONE); | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, | |||
| + CONTROL2_MODE | CONTROL2_TOG_RD_ONLY, | |||
| + CONTROL2_MODE_DRP | CONTROL2_TOG_RD_ONLY); | |||
| + break; | |||
| + default: | |||
| + dev_err(chip->dev, "%s: Unsupport cc mode %d\n", | |||
| + __func__, mode); | |||
| + return -EINVAL; | |||
| + break; | |||
| + } | |||
| + | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, CONTROL2_TOGGLE, | |||
| + CONTROL2_TOGGLE); | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static int tcpm_set_rx_enable(struct fusb30x_chip *chip, int enable) | |||
| +{ | |||
| + u8 val = 0; | |||
| + | |||
| + if (enable) { | |||
| + if (chip->cc_polarity == TYPEC_POLARITY_CC1) | |||
| + val |= SWITCHES0_MEAS_CC1; | |||
| + else | |||
| + val |= SWITCHES0_MEAS_CC2; | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, | |||
| + SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2, | |||
| + val); | |||
| + fusb302_flush_rx_fifo(chip); | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1, | |||
| + SWITCHES1_AUTO_CRC, SWITCHES1_AUTO_CRC); | |||
| + } else { | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, | |||
| + SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2, | |||
| + 0); | |||
| + regmap_update_bits(chip->regmap, | |||
| + FUSB_REG_SWITCHES1, SWITCHES1_AUTO_CRC, 0); | |||
| + } | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static int tcpm_set_msg_header(struct fusb30x_chip *chip) | |||
| +{ | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1, | |||
| + SWITCHES1_POWERROLE | SWITCHES1_DATAROLE, | |||
| + (chip->notify.power_role << 7) | | |||
| + (chip->notify.data_role << 4)); | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1, | |||
| + SWITCHES1_SPECREV, 2 << 5); | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static int tcpm_set_polarity(struct fusb30x_chip *chip, | |||
| + enum typec_cc_polarity polarity) | |||
| +{ | |||
| + u8 val = 0; | |||
| + | |||
| + if (chip->vconn_enabled) { | |||
| + if (polarity) | |||
| + val |= SWITCHES0_VCONN_CC1; | |||
| + else | |||
| + val |= SWITCHES0_VCONN_CC2; | |||
| + } | |||
| + | |||
| + if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) { | |||
| + if (polarity == TYPEC_POLARITY_CC1) | |||
| + val |= SWITCHES0_MEAS_CC1; | |||
| + else | |||
| + val |= SWITCHES0_MEAS_CC2; | |||
| + } else { | |||
| + if (polarity == TYPEC_POLARITY_CC1) | |||
| + val |= SWITCHES0_MEAS_CC1 | SWITCHES0_PU_EN1; | |||
| + else | |||
| + val |= SWITCHES0_MEAS_CC2 | SWITCHES0_PU_EN2; | |||
| + } | |||
| + | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, | |||
| + SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2 | | |||
| + SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 | | |||
| + SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2, | |||
| + val); | |||
| + | |||
| + val = 0; | |||
| + if (polarity == TYPEC_POLARITY_CC1) | |||
| + val |= SWITCHES1_TXCC1; | |||
| + else | |||
| + val |= SWITCHES1_TXCC2; | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1, | |||
| + SWITCHES1_TXCC1 | SWITCHES1_TXCC2, | |||
| + val); | |||
| + | |||
| + chip->cc_polarity = polarity; | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static int tcpm_set_vconn(struct fusb30x_chip *chip, int enable) | |||
| +{ | |||
| + u8 val = 0; | |||
| + | |||
| + if (enable) { | |||
| + if (chip->cc_polarity == TYPEC_POLARITY_CC1) | |||
| + val = SWITCHES0_VCONN_CC2; | |||
| + else | |||
| + val = SWITCHES0_VCONN_CC1; | |||
| + } | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, | |||
| + SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2, | |||
| + val); | |||
| + chip->vconn_enabled = (bool)enable; | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static void fusb302_pd_reset(struct fusb30x_chip *chip) | |||
| +{ | |||
| + regmap_write(chip->regmap, FUSB_REG_RESET, RESET_PD_RESET); | |||
| + regmap_reinit_cache(chip->regmap, &fusb302_regmap_config); | |||
| +} | |||
| + | |||
| +static void tcpm_select_rp_value(struct fusb30x_chip *chip, u32 rp) | |||
| +{ | |||
| + u32 control0_reg; | |||
| + | |||
| + regmap_read(chip->regmap, FUSB_REG_CONTROL0, &control0_reg); | |||
| + | |||
| + control0_reg &= ~CONTROL0_HOST_CUR; | |||
| + /* | |||
| + * according to the host current, the compare value is different | |||
| + * Fusb302 datasheet Table 3 | |||
| + */ | |||
| + switch (rp) { | |||
| + /* | |||
| + * host pull up current is 80ua , high voltage is 1.596v, | |||
| + * low is 0.21v | |||
| + */ | |||
| + case TYPEC_RP_USB: | |||
| + chip->cc_meas_high = 0x26; | |||
| + chip->cc_meas_low = 0x5; | |||
| + control0_reg |= CONTROL0_HOST_CUR_USB; | |||
| + break; | |||
| + /* | |||
| + * host pull up current is 330ua , high voltage is 2.604v, | |||
| + * low is 0.798v | |||
| + */ | |||
| + case TYPEC_RP_3A0: | |||
| + chip->cc_meas_high = 0x3e; | |||
| + chip->cc_meas_low = 0x13; | |||
| + control0_reg |= CONTROL0_HOST_CUR_3A0; | |||
| + break; | |||
| + /* | |||
| + * host pull up current is 180ua , high voltage is 1.596v, | |||
| + * low is 0.42v | |||
| + */ | |||
| + case TYPEC_RP_1A5: | |||
| + default: | |||
| + chip->cc_meas_high = 0x26; | |||
| + chip->cc_meas_low = 0xa; | |||
| + control0_reg |= CONTROL0_HOST_CUR_1A5; | |||
| + break; | |||
| + } | |||
| + | |||
| + regmap_write(chip->regmap, FUSB_REG_CONTROL0, control0_reg); | |||
| +} | |||
| + | |||
| +static int tcpm_check_vbus(struct fusb30x_chip *chip) | |||
| +{ | |||
| + u32 val; | |||
| + | |||
| + /* Read status register */ | |||
| + regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); | |||
| + | |||
| + return (val & STATUS0_VBUSOK) ? 1 : 0; | |||
| +} | |||
| + | |||
| +static void tcpm_init(struct fusb30x_chip *chip) | |||
| +{ | |||
| + u8 val; | |||
| + u32 tmp; | |||
| + | |||
| + regmap_read(chip->regmap, FUSB_REG_DEVICEID, &tmp); | |||
| + chip->chip_id = (u8)tmp; | |||
| + platform_set_vbus_lvl_enable(chip, 0, 0); | |||
| + chip->notify.is_cc_connected = false; | |||
| + chip->cc_state = 0; | |||
| + | |||
| + /* restore default settings */ | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_RESET, RESET_SW_RESET, | |||
| + RESET_SW_RESET); | |||
| + fusb302_pd_reset(chip); | |||
| + /* set auto_retry and number of retries */ | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_CONTROL3, | |||
| + CONTROL3_AUTO_RETRY | CONTROL3_N_RETRIES, | |||
| + CONTROL3_AUTO_RETRY | CONTROL3_N_RETRIES), | |||
| + | |||
| + /* set interrupts */ | |||
| + val = 0xff; | |||
| + val &= ~(MASK_M_COLLISION | MASK_M_ALERT | MASK_M_VBUSOK); | |||
| + regmap_write(chip->regmap, FUSB_REG_MASK, val); | |||
| + | |||
| + val = 0xff; | |||
| + val &= ~(MASKA_M_RETRYFAIL | MASKA_M_HARDSENT | MASKA_M_TXSENT | | |||
| + MASKA_M_HARDRST | MASKA_M_TOGDONE); | |||
| + regmap_write(chip->regmap, FUSB_REG_MASKA, val); | |||
| + | |||
| + val = ~MASKB_M_GCRCSEND; | |||
| + regmap_write(chip->regmap, FUSB_REG_MASKB, val); | |||
| + | |||
| + tcpm_select_rp_value(chip, TYPEC_RP_1A5); | |||
| + /* Interrupts Enable */ | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_CONTROL0, CONTROL0_INT_MASK, | |||
| + ~CONTROL0_INT_MASK); | |||
| + | |||
| + tcpm_set_vconn(chip, 0); | |||
| + | |||
| + regmap_write(chip->regmap, FUSB_REG_POWER, 0xf); | |||
| +} | |||
| + | |||
| +static void pd_execute_hard_reset(struct fusb30x_chip *chip) | |||
| +{ | |||
| + chip->msg_id = 0; | |||
| + chip->vdm_state = VDM_STATE_DISCOVERY_ID; | |||
| + if (chip->notify.power_role) | |||
| + set_state(chip, policy_src_transition_default); | |||
| + else | |||
| + set_state(chip, policy_snk_transition_default); | |||
| +} | |||
| + | |||
| +static void tcpc_alert(struct fusb30x_chip *chip, u32 *evt) | |||
| +{ | |||
| + int interrupt, interrupta, interruptb; | |||
| + u32 val; | |||
| + static int retry; | |||
| + | |||
| + regmap_read(chip->regmap, FUSB_REG_INTERRUPT, &interrupt); | |||
| + regmap_read(chip->regmap, FUSB_REG_INTERRUPTA, &interrupta); | |||
| + regmap_read(chip->regmap, FUSB_REG_INTERRUPTB, &interruptb); | |||
| + | |||
| + if ((interrupt & INTERRUPT_COMP_CHNG) && | |||
| + (!(chip->cc_state & CC_STATE_TOGSS_IS_UFP))) { | |||
| + regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); | |||
| + if (val & STATUS0_COMP) | |||
| + *evt |= EVENT_CC; | |||
| + } | |||
| + | |||
| + if (interrupt & INTERRUPT_VBUSOK) { | |||
| + if (chip->notify.is_cc_connected) | |||
| + *evt |= EVENT_CC; | |||
| + } | |||
| + | |||
| + if (interrupta & INTERRUPTA_TOGDONE) { | |||
| + *evt |= EVENT_CC; | |||
| + regmap_read(chip->regmap, FUSB_REG_STATUS1A, &val); | |||
| + chip->cc_state = ((u8)val >> 3) & 0x07; | |||
| + | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, | |||
| + CONTROL2_TOGGLE, | |||
| + 0); | |||
| + } | |||
| + | |||
| + if (interrupta & INTERRUPTA_TXSENT) { | |||
| + *evt |= EVENT_TX; | |||
| + chip->tx_state = tx_success; | |||
| + } | |||
| + | |||
| + if (interruptb & INTERRUPTB_GCRCSENT) | |||
| + *evt |= EVENT_RX; | |||
| + | |||
| + if (interrupta & INTERRUPTA_HARDRST) { | |||
| + fusb302_pd_reset(chip); | |||
| + pd_execute_hard_reset(chip); | |||
| + *evt |= EVENT_REC_RESET; | |||
| + } | |||
| + | |||
| + if (interrupta & INTERRUPTA_RETRYFAIL) { | |||
| + *evt |= EVENT_TX; | |||
| + chip->tx_state = tx_failed; | |||
| + } | |||
| + | |||
| + if (interrupta & INTERRUPTA_HARDSENT) { | |||
| + /* | |||
| + * The fusb PD should be reset once to sync adapter PD | |||
| + * signal after fusb sent hard reset cmd.This is not PD | |||
| + * device if reset failed. | |||
| + */ | |||
| + if (!retry) { | |||
| + retry = 1; | |||
| + fusb302_pd_reset(chip); | |||
| + pd_execute_hard_reset(chip); | |||
| + } else { | |||
| + retry = 0; | |||
| + chip->tx_state = tx_success; | |||
| + chip->timer_state = T_DISABLED; | |||
| + *evt |= EVENT_TX; | |||
| + } | |||
| + } | |||
| +} | |||
| + | |||
| +static void mux_alert(struct fusb30x_chip *chip, u32 *evt) | |||
| +{ | |||
| + if (!chip->timer_mux) { | |||
| + *evt |= EVENT_TIMER_MUX; | |||
| + chip->timer_mux = T_DISABLED; | |||
| + } | |||
| + | |||
| + if (!chip->timer_state) { | |||
| + *evt |= EVENT_TIMER_STATE; | |||
| + chip->timer_state = T_DISABLED; | |||
| + } | |||
| + | |||
| + if (chip->work_continue) { | |||
| + *evt |= chip->work_continue; | |||
| + chip->work_continue = 0; | |||
| + } | |||
| +} | |||
| + | |||
| +static void set_state_unattached(struct fusb30x_chip *chip) | |||
| +{ | |||
| + dev_info(chip->dev, "connection has disconnected\n"); | |||
| + tcpm_init(chip); | |||
| + tcpm_set_rx_enable(chip, 0); | |||
| + set_state(chip, unattached); | |||
| + tcpm_set_cc(chip, chip->role); | |||
| + | |||
| + /* claer notify_info */ | |||
| + memset(&chip->notify, 0, sizeof(struct notify_info)); | |||
| + platform_fusb_notify(chip); | |||
| + | |||
| + if (chip->gpio_discharge) | |||
| + gpiod_set_value(chip->gpio_discharge, 1); | |||
| + msleep(100); | |||
| + if (chip->gpio_discharge) | |||
| + gpiod_set_value(chip->gpio_discharge, 0); | |||
| + | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_MASK, | |||
| + MASK_M_COMP_CHNG, MASK_M_COMP_CHNG); | |||
| + chip->try_role_complete = false; | |||
| +} | |||
| + | |||
| +static void set_mesg(struct fusb30x_chip *chip, int cmd, int is_DMT) | |||
| +{ | |||
| + int i; | |||
| + struct PD_CAP_INFO *pd_cap_info = &chip->pd_cap_info; | |||
| + | |||
| + chip->send_head = ((chip->msg_id & 0x7) << 9) | | |||
| + ((chip->notify.power_role & 0x1) << 8) | | |||
| + (1 << 6) | | |||
| + ((chip->notify.data_role & 0x1) << 5); | |||
| + | |||
| + if (is_DMT) { | |||
| + switch (cmd) { | |||
| + case DMT_SOURCECAPABILITIES: | |||
| + chip->send_head |= ((chip->n_caps_used & 0x3) << 12) | (cmd & 0xf); | |||
| + | |||
| + for (i = 0; i < chip->n_caps_used; i++) { | |||
| + chip->send_load[i] = (pd_cap_info->supply_type << 30) | | |||
| + (pd_cap_info->dual_role_power << 29) | | |||
| + (pd_cap_info->usb_suspend_support << 28) | | |||
| + (pd_cap_info->externally_powered << 27) | | |||
| + (pd_cap_info->usb_communications_cap << 26) | | |||
| + (pd_cap_info->data_role_swap << 25) | | |||
| + (pd_cap_info->peak_current << 20) | | |||
| + (chip->source_power_supply[i] << 10) | | |||
| + (chip->source_max_current[i]); | |||
| + } | |||
| + break; | |||
| + case DMT_REQUEST: | |||
| + chip->send_head |= ((1 << 12) | (cmd & 0xf)); | |||
| + /* send request with FVRDO */ | |||
| + chip->send_load[0] = (chip->pos_power << 28) | | |||
| + (0 << 27) | | |||
| + (1 << 26) | | |||
| + (0 << 25) | | |||
| + (0 << 24); | |||
| + | |||
| + switch (CAP_POWER_TYPE(chip->rec_load[chip->pos_power - 1])) { | |||
| + case 0: | |||
| + /* Fixed Supply */ | |||
| + chip->send_load[0] |= ((CAP_FPDO_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 10) & 0x3ff); | |||
| + chip->send_load[0] |= (CAP_FPDO_CURRENT(chip->rec_load[chip->pos_power - 1]) & 0x3ff); | |||
| + break; | |||
| + case 1: | |||
| + /* Battery */ | |||
| + chip->send_load[0] |= ((CAP_VPDO_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 10) & 0x3ff); | |||
| + chip->send_load[0] |= (CAP_VPDO_CURRENT(chip->rec_load[chip->pos_power - 1]) & 0x3ff); | |||
| + break; | |||
| + default: | |||
| + /* not meet battery caps */ | |||
| + break; | |||
| + } | |||
| + break; | |||
| + case DMT_SINKCAPABILITIES: | |||
| + break; | |||
| + case DMT_VENDERDEFINED: | |||
| + break; | |||
| + default: | |||
| + break; | |||
| + } | |||
| + } else { | |||
| + chip->send_head |= (cmd & 0xf); | |||
| + } | |||
| +} | |||
| + | |||
| +/* | |||
| + * This algorithm defaults to choosing higher pin config over lower ones in | |||
| + * order to prefer multi-function if desired. | |||
| + * | |||
| + * NAME | SIGNALING | OUTPUT TYPE | MULTI-FUNCTION | PIN CONFIG | |||
| + * ------------------------------------------------------------- | |||
| + * A | USB G2 | ? | no | 00_0001 | |||
| + * B | USB G2 | ? | yes | 00_0010 | |||
| + * C | DP | CONVERTED | no | 00_0100 | |||
| + * D | PD | CONVERTED | yes | 00_1000 | |||
| + * E | DP | DP | no | 01_0000 | |||
| + * F | PD | DP | yes | 10_0000 | |||
| + * | |||
| + * if UFP has NOT asserted multi-function preferred code masks away B/D/F | |||
| + * leaving only A/C/E. For single-output dongles that should leave only one | |||
| + * possible pin config depending on whether its a converter DP->(VGA|HDMI) or DP | |||
| + * output. If UFP is a USB-C receptacle it may assert C/D/E/F. The DFP USB-C | |||
| + * receptacle must always choose C/D in those cases. | |||
| + */ | |||
| +static int pd_dfp_dp_get_pin_assignment(struct fusb30x_chip *chip, | |||
| + uint32_t caps, uint32_t status) | |||
| +{ | |||
| + uint32_t pin_caps; | |||
| + | |||
| + /* revisit with DFP that can be a sink */ | |||
| + pin_caps = PD_DP_PIN_CAPS(caps); | |||
| + | |||
| + /* if don't want multi-function then ignore those pin configs */ | |||
| + if (!PD_VDO_DPSTS_MF_PREF(status)) | |||
| + pin_caps &= ~MODE_DP_PIN_MF_MASK; | |||
| + | |||
| + /* revisit if DFP drives USB Gen 2 signals */ | |||
| + if (PD_DP_SIGNAL_GEN2(caps)) | |||
| + pin_caps &= ~MODE_DP_PIN_DP_MASK; | |||
| + else | |||
| + pin_caps &= ~MODE_DP_PIN_BR2_MASK; | |||
| + | |||
| + /* if C/D present they have precedence over E/F for USB-C->USB-C */ | |||
| + if (pin_caps & (MODE_DP_PIN_C | MODE_DP_PIN_D)) | |||
| + pin_caps &= ~(MODE_DP_PIN_E | MODE_DP_PIN_F); | |||
| + | |||
| + /* returns undefined for zero */ | |||
| + if (!pin_caps) | |||
| + return 0; | |||
| + | |||
| + /* choosing higher pin config over lower ones */ | |||
| + return 1 << (31 - __builtin_clz(pin_caps)); | |||
| +} | |||
| + | |||
| +static void set_vdm_mesg(struct fusb30x_chip *chip, int cmd, int type, int mode) | |||
| +{ | |||
| + chip->send_head = (chip->msg_id & 0x7) << 9; | |||
| + chip->send_head |= (chip->notify.power_role & 0x1) << 8; | |||
| + | |||
| + chip->send_head = ((chip->msg_id & 0x7) << 9) | | |||
| + ((chip->notify.power_role & 0x1) << 8) | | |||
| + (1 << 6) | | |||
| + ((chip->notify.data_role & 0x1) << 5) | | |||
| + (DMT_VENDERDEFINED & 0xf); | |||
| + | |||
| + chip->send_load[0] = (1 << 15) | | |||
| + (0 << 13) | | |||
| + (type << 6) | | |||
| + (cmd); | |||
| + | |||
| + switch (cmd) { | |||
| + case VDM_DISCOVERY_ID: | |||
| + case VDM_DISCOVERY_SVIDS: | |||
| + case VDM_ATTENTION: | |||
| + chip->send_load[0] |= (0xff00 << 16); | |||
| + chip->send_head |= (1 << 12); | |||
| + break; | |||
| + case VDM_DISCOVERY_MODES: | |||
| + chip->send_load[0] |= | |||
| + (chip->vdm_svid[chip->val_tmp >> 1] << 16); | |||
| + chip->send_head |= (1 << 12); | |||
| + break; | |||
| + case VDM_ENTER_MODE: | |||
| + chip->send_head |= (1 << 12); | |||
| + chip->send_load[0] |= (mode << 8) | (0xff01 << 16); | |||
| + break; | |||
| + case VDM_EXIT_MODE: | |||
| + chip->send_head |= (1 << 12); | |||
| + chip->send_load[0] |= (0x0f << 8) | (0xff01 << 16); | |||
| + break; | |||
| + case VDM_DP_STATUS_UPDATE: | |||
| + chip->send_head |= (2 << 12); | |||
| + chip->send_load[0] |= (1 << 8) | (0xff01 << 16); | |||
| + chip->send_load[1] = 5; | |||
| + break; | |||
| + case VDM_DP_CONFIG: | |||
| + chip->send_head |= (2 << 12); | |||
| + chip->send_load[0] |= (1 << 8) | (0xff01 << 16); | |||
| + | |||
| + chip->notify.pin_assignment_def = | |||
| + pd_dfp_dp_get_pin_assignment(chip, chip->notify.dp_caps, | |||
| + chip->notify.dp_status); | |||
| + | |||
| + chip->send_load[1] = (chip->notify.pin_assignment_def << 8) | | |||
| + (1 << 2) | 2; | |||
| + dev_dbg(chip->dev, "DisplayPort Configurations: 0x%08x\n", | |||
| + chip->send_load[1]); | |||
| + break; | |||
| + default: | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static enum tx_state policy_send_hardrst(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + switch (chip->tx_state) { | |||
| + case 0: | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_CONTROL3, | |||
| + CONTROL3_SEND_HARDRESET, | |||
| + CONTROL3_SEND_HARDRESET); | |||
| + chip->tx_state = tx_busy; | |||
| + chip->timer_state = T_BMC_TIMEOUT; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + break; | |||
| + default: | |||
| + if (evt & EVENT_TIMER_STATE) | |||
| + chip->tx_state = tx_success; | |||
| + break; | |||
| + } | |||
| + return chip->tx_state; | |||
| +} | |||
| + | |||
| +static enum tx_state policy_send_data(struct fusb30x_chip *chip) | |||
| +{ | |||
| + u8 senddata[40]; | |||
| + int pos = 0; | |||
| + u8 len; | |||
| + | |||
| + switch (chip->tx_state) { | |||
| + case 0: | |||
| + senddata[pos++] = FUSB_TKN_SYNC1; | |||
| + senddata[pos++] = FUSB_TKN_SYNC1; | |||
| + senddata[pos++] = FUSB_TKN_SYNC1; | |||
| + senddata[pos++] = FUSB_TKN_SYNC2; | |||
| + | |||
| + len = PD_HEADER_CNT(chip->send_head) << 2; | |||
| + senddata[pos++] = FUSB_TKN_PACKSYM | ((len + 2) & 0x1f); | |||
| + | |||
| + senddata[pos++] = chip->send_head & 0xff; | |||
| + senddata[pos++] = (chip->send_head >> 8) & 0xff; | |||
| + | |||
| + memcpy(&senddata[pos], chip->send_load, len); | |||
| + pos += len; | |||
| + | |||
| + senddata[pos++] = FUSB_TKN_JAMCRC; | |||
| + senddata[pos++] = FUSB_TKN_EOP; | |||
| + senddata[pos++] = FUSB_TKN_TXOFF; | |||
| + senddata[pos++] = FUSB_TKN_TXON; | |||
| + | |||
| + regmap_raw_write(chip->regmap, FUSB_REG_FIFO, senddata, pos); | |||
| + chip->tx_state = tx_busy; | |||
| + break; | |||
| + | |||
| + default: | |||
| + /* wait Tx result */ | |||
| + break; | |||
| + } | |||
| + | |||
| + return chip->tx_state; | |||
| +} | |||
| + | |||
| +static void process_vdm_msg(struct fusb30x_chip *chip) | |||
| +{ | |||
| + u32 vdm_header = chip->rec_load[0]; | |||
| + int i; | |||
| + u32 tmp; | |||
| + | |||
| + /* can't procee unstructed vdm msg */ | |||
| + if (!GET_VDMHEAD_STRUCT_TYPE(vdm_header)) { | |||
| + dev_warn(chip->dev, "unknown unstructed vdm message\n"); | |||
| + return; | |||
| + } | |||
| + | |||
| + switch (GET_VDMHEAD_CMD_TYPE(vdm_header)) { | |||
| + case VDM_TYPE_INIT: | |||
| + switch (GET_VDMHEAD_CMD(vdm_header)) { | |||
| + case VDM_ATTENTION: | |||
| + chip->notify.dp_status = GET_DP_STATUS(chip->rec_load[1]); | |||
| + dev_info(chip->dev, "attention, dp_status %x\n", | |||
| + chip->rec_load[1]); | |||
| + chip->notify.attention = true; | |||
| + platform_fusb_notify(chip); | |||
| + break; | |||
| + default: | |||
| + dev_warn(chip->dev, "rec unknown init vdm msg\n"); | |||
| + break; | |||
| + } | |||
| + break; | |||
| + case VDM_TYPE_ACK: | |||
| + switch (GET_VDMHEAD_CMD(vdm_header)) { | |||
| + case VDM_DISCOVERY_ID: | |||
| + chip->vdm_id = chip->rec_load[1]; | |||
| + break; | |||
| + case VDM_DISCOVERY_SVIDS: | |||
| + for (i = 0; i < 6; i++) { | |||
| + tmp = (chip->rec_load[i + 1] >> 16) & | |||
| + 0x0000ffff; | |||
| + if (tmp) { | |||
| + chip->vdm_svid[i * 2] = tmp; | |||
| + chip->vdm_svid_num++; | |||
| + } else { | |||
| + break; | |||
| + } | |||
| + | |||
| + tmp = (chip->rec_load[i + 1] & 0x0000ffff); | |||
| + if (tmp) { | |||
| + chip->vdm_svid[i * 2 + 1] = tmp; | |||
| + chip->vdm_svid_num++; | |||
| + } else { | |||
| + break; | |||
| + } | |||
| + } | |||
| + break; | |||
| + case VDM_DISCOVERY_MODES: | |||
| + /* indicate there are some vdo modes */ | |||
| + if (PD_HEADER_CNT(chip->rec_head) > 1) { | |||
| + /* | |||
| + * store mode config, | |||
| + * enter first mode default | |||
| + */ | |||
| + tmp = chip->rec_load[1]; | |||
| + | |||
| + if ((!((tmp >> 8) & 0x3f)) && | |||
| + (!((tmp >> 16) & 0x3f))) { | |||
| + chip->val_tmp |= 1; | |||
| + break; | |||
| + } | |||
| + chip->notify.dp_caps = chip->rec_load[1]; | |||
| + chip->notify.pin_assignment_def = 0; | |||
| + chip->notify.pin_assignment_support = | |||
| + PD_DP_PIN_CAPS(tmp); | |||
| + chip->val_tmp |= 1; | |||
| + dev_dbg(chip->dev, | |||
| + "DisplayPort Capabilities: 0x%08x\n", | |||
| + chip->rec_load[1]); | |||
| + } | |||
| + break; | |||
| + case VDM_ENTER_MODE: | |||
| + chip->val_tmp = 1; | |||
| + break; | |||
| + case VDM_DP_STATUS_UPDATE: | |||
| + chip->notify.dp_status = GET_DP_STATUS(chip->rec_load[1]); | |||
| + dev_dbg(chip->dev, "DisplayPort Status: 0x%08x\n", | |||
| + chip->rec_load[1]); | |||
| + chip->val_tmp = 1; | |||
| + break; | |||
| + case VDM_DP_CONFIG: | |||
| + chip->val_tmp = 1; | |||
| + dev_info(chip->dev, | |||
| + "DP config successful, pin_assignment 0x%x\n", | |||
| + chip->notify.pin_assignment_def); | |||
| + chip->notify.is_enter_mode = true; | |||
| + break; | |||
| + default: | |||
| + break; | |||
| + } | |||
| + break; | |||
| + case VDM_TYPE_NACK: | |||
| + dev_warn(chip->dev, "REC NACK for 0x%x\n", | |||
| + GET_VDMHEAD_CMD(vdm_header)); | |||
| + /* disable vdm */ | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static int vdm_send_discoveryid(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + int tmp; | |||
| + | |||
| + switch (chip->vdm_send_state) { | |||
| + case 0: | |||
| + set_vdm_mesg(chip, VDM_DISCOVERY_ID, VDM_TYPE_INIT, 0); | |||
| + chip->vdm_id = 0; | |||
| + chip->tx_state = 0; | |||
| + chip->vdm_send_state++; | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->vdm_send_state++; | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (tmp == tx_failed) { | |||
| + dev_warn(chip->dev, "VDM_DISCOVERY_ID send failed\n"); | |||
| + /* disable auto_vdm_machine */ | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + return -EPIPE; | |||
| + } | |||
| + | |||
| + if (chip->vdm_send_state != 2) | |||
| + break; | |||
| + default: | |||
| + if (chip->vdm_id) { | |||
| + chip->vdm_send_state = 0; | |||
| + return 0; | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + dev_warn(chip->dev, "VDM_DISCOVERY_ID time out\n"); | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + return -ETIMEDOUT; | |||
| + } | |||
| + break; | |||
| + } | |||
| + return -EINPROGRESS; | |||
| +} | |||
| + | |||
| +static int vdm_send_discoverysvid(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + int tmp; | |||
| + | |||
| + switch (chip->vdm_send_state) { | |||
| + case 0: | |||
| + set_vdm_mesg(chip, VDM_DISCOVERY_SVIDS, VDM_TYPE_INIT, 0); | |||
| + memset(chip->vdm_svid, 0, sizeof(chip->vdm_svid)); | |||
| + chip->vdm_svid_num = 0; | |||
| + chip->tx_state = 0; | |||
| + chip->vdm_send_state++; | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->vdm_send_state++; | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (tmp == tx_failed) { | |||
| + dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS send failed\n"); | |||
| + /* disable auto_vdm_machine */ | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + return -EPIPE; | |||
| + } | |||
| + | |||
| + if (chip->vdm_send_state != 2) | |||
| + break; | |||
| + default: | |||
| + if (chip->vdm_svid_num) { | |||
| + chip->vdm_send_state = 0; | |||
| + return 0; | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS time out\n"); | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + return -ETIMEDOUT; | |||
| + } | |||
| + break; | |||
| + } | |||
| + return -EINPROGRESS; | |||
| +} | |||
| + | |||
| +static int vdm_send_discoverymodes(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + int tmp; | |||
| + | |||
| + if ((chip->val_tmp >> 1) != chip->vdm_svid_num) { | |||
| + switch (chip->vdm_send_state) { | |||
| + case 0: | |||
| + set_vdm_mesg(chip, VDM_DISCOVERY_MODES, | |||
| + VDM_TYPE_INIT, 0); | |||
| + chip->tx_state = 0; | |||
| + chip->vdm_send_state++; | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->vdm_send_state++; | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (tmp == tx_failed) { | |||
| + dev_warn(chip->dev, | |||
| + "VDM_DISCOVERY_MODES send failed\n"); | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + return -EPIPE; | |||
| + } | |||
| + | |||
| + if (chip->vdm_send_state != 2) | |||
| + break; | |||
| + default: | |||
| + if (chip->val_tmp & 1) { | |||
| + chip->val_tmp &= 0xfe; | |||
| + chip->val_tmp += 2; | |||
| + chip->vdm_send_state = 0; | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + dev_warn(chip->dev, | |||
| + "VDM_DISCOVERY_MODES time out\n"); | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + return -ETIMEDOUT; | |||
| + } | |||
| + break; | |||
| + } | |||
| + } else { | |||
| + chip->val_tmp = 0; | |||
| + return 0; | |||
| + } | |||
| + | |||
| + return -EINPROGRESS; | |||
| +} | |||
| + | |||
| +static int vdm_send_entermode(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + int tmp; | |||
| + | |||
| + switch (chip->vdm_send_state) { | |||
| + case 0: | |||
| + set_vdm_mesg(chip, VDM_ENTER_MODE, VDM_TYPE_INIT, 1); | |||
| + chip->tx_state = 0; | |||
| + chip->vdm_send_state++; | |||
| + chip->notify.is_enter_mode = false; | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->vdm_send_state++; | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (tmp == tx_failed) { | |||
| + dev_warn(chip->dev, "VDM_ENTER_MODE send failed\n"); | |||
| + /* disable auto_vdm_machine */ | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + return -EPIPE; | |||
| + } | |||
| + | |||
| + if (chip->vdm_send_state != 2) | |||
| + break; | |||
| + default: | |||
| + if (chip->val_tmp) { | |||
| + chip->val_tmp = 0; | |||
| + chip->vdm_send_state = 0; | |||
| + return 0; | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + dev_warn(chip->dev, "VDM_ENTER_MODE time out\n"); | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + return -ETIMEDOUT; | |||
| + } | |||
| + break; | |||
| + } | |||
| + return -EINPROGRESS; | |||
| +} | |||
| + | |||
| +static int vdm_send_getdpstatus(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + int tmp; | |||
| + | |||
| + switch (chip->vdm_send_state) { | |||
| + case 0: | |||
| + set_vdm_mesg(chip, VDM_DP_STATUS_UPDATE, VDM_TYPE_INIT, 1); | |||
| + chip->tx_state = 0; | |||
| + chip->vdm_send_state++; | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->vdm_send_state++; | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (tmp == tx_failed) { | |||
| + dev_warn(chip->dev, | |||
| + "VDM_DP_STATUS_UPDATE send failed\n"); | |||
| + /* disable auto_vdm_machine */ | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + return -EPIPE; | |||
| + } | |||
| + | |||
| + if (chip->vdm_send_state != 2) | |||
| + break; | |||
| + default: | |||
| + if (chip->val_tmp) { | |||
| + chip->val_tmp = 0; | |||
| + chip->vdm_send_state = 0; | |||
| + return 0; | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + dev_warn(chip->dev, "VDM_DP_STATUS_UPDATE time out\n"); | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + return -ETIMEDOUT; | |||
| + } | |||
| + break; | |||
| + } | |||
| + return -EINPROGRESS; | |||
| +} | |||
| + | |||
| +static int vdm_send_dpconfig(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + int tmp; | |||
| + | |||
| + switch (chip->vdm_send_state) { | |||
| + case 0: | |||
| + set_vdm_mesg(chip, VDM_DP_CONFIG, VDM_TYPE_INIT, 0); | |||
| + chip->tx_state = 0; | |||
| + chip->vdm_send_state++; | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->vdm_send_state++; | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (tmp == tx_failed) { | |||
| + dev_warn(chip->dev, "vdm_send_dpconfig send failed\n"); | |||
| + /* disable auto_vdm_machine */ | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + return -EPIPE; | |||
| + } | |||
| + | |||
| + if (chip->vdm_send_state != 2) | |||
| + break; | |||
| + default: | |||
| + if (chip->val_tmp) { | |||
| + chip->val_tmp = 0; | |||
| + chip->vdm_send_state = 0; | |||
| + return 0; | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + dev_warn(chip->dev, "vdm_send_dpconfig time out\n"); | |||
| + chip->vdm_state = VDM_STATE_ERR; | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + return -ETIMEDOUT; | |||
| + } | |||
| + break; | |||
| + } | |||
| + return -EINPROGRESS; | |||
| +} | |||
| + | |||
| +/* without break if success */ | |||
| +#define AUTO_VDM_HANDLE(func, chip, evt, conditions) \ | |||
| +do { \ | |||
| + conditions = func(chip, evt); \ | |||
| + if (!conditions) { \ | |||
| + chip->vdm_state++; \ | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; \ | |||
| + } else { \ | |||
| + if (conditions != -EINPROGRESS) \ | |||
| + chip->vdm_state = VDM_STATE_ERR; \ | |||
| + } \ | |||
| +} while (0) | |||
| + | |||
| +static void auto_vdm_machine(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + int conditions; | |||
| + | |||
| + switch (chip->vdm_state) { | |||
| + case VDM_STATE_DISCOVERY_ID: | |||
| + AUTO_VDM_HANDLE(vdm_send_discoveryid, chip, evt, conditions); | |||
| + break; | |||
| + case VDM_STATE_DISCOVERY_SVID: | |||
| + AUTO_VDM_HANDLE(vdm_send_discoverysvid, chip, evt, conditions); | |||
| + break; | |||
| + case VDM_STATE_DISCOVERY_MODES: | |||
| + AUTO_VDM_HANDLE(vdm_send_discoverymodes, chip, evt, conditions); | |||
| + break; | |||
| + case VDM_STATE_ENTER_MODE: | |||
| + AUTO_VDM_HANDLE(vdm_send_entermode, chip, evt, conditions); | |||
| + break; | |||
| + case VDM_STATE_UPDATE_STATUS: | |||
| + AUTO_VDM_HANDLE(vdm_send_getdpstatus, chip, evt, conditions); | |||
| + break; | |||
| + case VDM_STATE_DP_CONFIG: | |||
| + AUTO_VDM_HANDLE(vdm_send_dpconfig, chip, evt, conditions); | |||
| + break; | |||
| + case VDM_STATE_NOTIFY: | |||
| + platform_fusb_notify(chip); | |||
| + chip->vdm_state = VDM_STATE_READY; | |||
| + break; | |||
| + default: | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_disabled(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + /* Do nothing */ | |||
| +} | |||
| + | |||
| +static void fusb_state_unattached(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + chip->notify.is_cc_connected = false; | |||
| + chip->is_pd_support = false; | |||
| + | |||
| + if ((evt & EVENT_CC) && chip->cc_state) { | |||
| + if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) | |||
| + set_state(chip, attach_wait_sink); | |||
| + else | |||
| + set_state(chip, attach_wait_source); | |||
| + | |||
| + chip->vbus_begin = tcpm_check_vbus(chip); | |||
| + | |||
| + tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ? | |||
| + TYPEC_POLARITY_CC1 : | |||
| + TYPEC_POLARITY_CC2); | |||
| + tcpm_get_cc(chip, &chip->cc1, &chip->cc2); | |||
| + chip->debounce_cnt = 0; | |||
| + chip->timer_mux = 2; | |||
| + fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux); | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_try_attach_set(struct fusb30x_chip *chip, | |||
| + enum role_mode mode) | |||
| +{ | |||
| + if (mode == ROLE_MODE_NONE || mode == ROLE_MODE_DRP || | |||
| + mode == ROLE_MODE_ASS) | |||
| + return; | |||
| + | |||
| + tcpm_init(chip); | |||
| + tcpm_set_cc(chip, (mode == ROLE_MODE_DFP) ? | |||
| + ROLE_MODE_DFP : ROLE_MODE_UFP); | |||
| + chip->timer_mux = T_PD_TRY_DRP; | |||
| + fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux); | |||
| + set_state(chip, (mode == ROLE_MODE_DFP) ? | |||
| + attach_try_src : attach_try_snk); | |||
| +} | |||
| + | |||
| +static void fusb_state_attach_wait_sink(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + int cc1, cc2; | |||
| + | |||
| + if (evt & EVENT_TIMER_MUX) { | |||
| + if (tcpm_check_vbus(chip)) { | |||
| + chip->timer_mux = T_DISABLED; | |||
| + if (chip->role == ROLE_MODE_DRP && | |||
| + chip->try_role == ROLE_MODE_DFP && | |||
| + !chip->try_role_complete) { | |||
| + fusb_state_try_attach_set(chip, ROLE_MODE_DFP); | |||
| + return; | |||
| + } else if (chip->try_role_complete) { | |||
| + chip->timer_mux = T_PD_SOURCE_ON; | |||
| + fusb_timer_start(&chip->timer_mux_machine, | |||
| + chip->timer_mux); | |||
| + set_state(chip, attached_sink); | |||
| + return; | |||
| + } | |||
| + } | |||
| + | |||
| + tcpm_get_cc(chip, &cc1, &cc2); | |||
| + | |||
| + if ((chip->cc1 == cc1) && (chip->cc2 == cc2)) { | |||
| + chip->debounce_cnt++; | |||
| + } else { | |||
| + chip->cc1 = cc1; | |||
| + chip->cc2 = cc2; | |||
| + chip->debounce_cnt = 0; | |||
| + } | |||
| + | |||
| + if (chip->debounce_cnt > N_DEBOUNCE_CNT) { | |||
| + chip->timer_mux = T_DISABLED; | |||
| + if ((chip->cc1 == TYPEC_CC_VOLT_RP && | |||
| + chip->cc2 == TYPEC_CC_VOLT_OPEN) || | |||
| + (chip->cc2 == TYPEC_CC_VOLT_RP && | |||
| + chip->cc1 == TYPEC_CC_VOLT_OPEN)) { | |||
| + chip->timer_mux = T_PD_SOURCE_ON; | |||
| + fusb_timer_start(&chip->timer_mux_machine, | |||
| + chip->timer_mux); | |||
| + set_state(chip, attached_sink); | |||
| + } else { | |||
| + set_state_unattached(chip); | |||
| + } | |||
| + return; | |||
| + } | |||
| + | |||
| + chip->timer_mux = 2; | |||
| + fusb_timer_start(&chip->timer_mux_machine, | |||
| + chip->timer_mux); | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_attach_wait_source(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + int cc1, cc2; | |||
| + | |||
| + if (evt & EVENT_TIMER_MUX) { | |||
| + tcpm_get_cc(chip, &cc1, &cc2); | |||
| + | |||
| + if ((chip->cc1 == cc1) && (chip->cc2 == cc2)) { | |||
| + chip->debounce_cnt++; | |||
| + } else { | |||
| + chip->cc1 = cc1; | |||
| + chip->cc2 = cc2; | |||
| + chip->debounce_cnt = 0; | |||
| + } | |||
| + | |||
| + if (chip->debounce_cnt > N_DEBOUNCE_CNT) { | |||
| + if (((!chip->cc1) || (!chip->cc2)) && | |||
| + ((chip->cc1 == TYPEC_CC_VOLT_RD) || | |||
| + (chip->cc2 == TYPEC_CC_VOLT_RD))) { | |||
| + if (chip->role == ROLE_MODE_DRP && | |||
| + chip->try_role == ROLE_MODE_UFP && | |||
| + !chip->try_role_complete) | |||
| + fusb_state_try_attach_set(chip, | |||
| + ROLE_MODE_UFP); | |||
| + else | |||
| + set_state(chip, attached_source); | |||
| + } else { | |||
| + set_state_unattached(chip); | |||
| + } | |||
| + return; | |||
| + } | |||
| + | |||
| + chip->timer_mux = 2; | |||
| + fusb_timer_start(&chip->timer_mux_machine, | |||
| + chip->timer_mux); | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_attached_source(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + platform_set_vbus_lvl_enable(chip, 1, 0); | |||
| + tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ? | |||
| + TYPEC_POLARITY_CC1 : TYPEC_POLARITY_CC2); | |||
| + tcpm_set_vconn(chip, 1); | |||
| + | |||
| + chip->notify.is_cc_connected = true; | |||
| + | |||
| + chip->notify.power_role = POWER_ROLE_SOURCE; | |||
| + chip->notify.data_role = DATA_ROLE_DFP; | |||
| + chip->hardrst_count = 0; | |||
| + set_state(chip, policy_src_startup); | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_MASK, MASK_M_COMP_CHNG, 0); | |||
| + dev_info(chip->dev, "CC connected in %s as DFP\n", | |||
| + chip->cc_polarity ? "CC1" : "CC2"); | |||
| +} | |||
| + | |||
| +static void fusb_state_attached_sink(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + if (tcpm_check_vbus(chip)) { | |||
| + chip->timer_mux = T_DISABLED; | |||
| + chip->timer_state = T_DISABLED; | |||
| + if (!chip->try_role_complete && | |||
| + chip->try_role == ROLE_MODE_DFP && | |||
| + chip->role == ROLE_MODE_DRP) { | |||
| + fusb_state_try_attach_set(chip, ROLE_MODE_DFP); | |||
| + return; | |||
| + } | |||
| + | |||
| + chip->try_role_complete = true; | |||
| + chip->notify.is_cc_connected = true; | |||
| + chip->notify.power_role = POWER_ROLE_SINK; | |||
| + chip->notify.data_role = DATA_ROLE_UFP; | |||
| + chip->hardrst_count = 0; | |||
| + set_state(chip, policy_snk_startup); | |||
| + dev_info(chip->dev, "CC connected in %s as UFP\n", | |||
| + chip->cc_polarity ? "CC1" : "CC2"); | |||
| + return; | |||
| + } else if (evt & EVENT_TIMER_MUX) { | |||
| + set_state_unattached(chip); | |||
| + return; | |||
| + } | |||
| + | |||
| + chip->timer_state = 2; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| +} | |||
| + | |||
| +static void fusb_state_try_attach(struct fusb30x_chip *chip, u32 evt, | |||
| + enum role_mode mode) | |||
| +{ | |||
| + if ((evt & EVENT_CC) && chip->cc_state) { | |||
| + chip->try_role_complete = true; | |||
| + if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) | |||
| + set_state(chip, (mode == ROLE_MODE_UFP) ? | |||
| + attach_wait_sink : error_recovery); | |||
| + else | |||
| + set_state(chip, (mode == ROLE_MODE_DFP) ? | |||
| + attach_wait_source : error_recovery); | |||
| + | |||
| + tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ? | |||
| + TYPEC_POLARITY_CC1 : | |||
| + TYPEC_POLARITY_CC2); | |||
| + tcpm_get_cc(chip, &chip->cc1, &chip->cc2); | |||
| + chip->debounce_cnt = 0; | |||
| + chip->timer_mux = 2; | |||
| + fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux); | |||
| + } else if (evt & EVENT_TIMER_MUX) { | |||
| + if (!chip->try_role_complete) { | |||
| + chip->try_role_complete = true; | |||
| + fusb_state_try_attach_set(chip, | |||
| + (mode == ROLE_MODE_DFP) ? | |||
| + ROLE_MODE_UFP : | |||
| + ROLE_MODE_DFP); | |||
| + } else { | |||
| + set_state(chip, error_recovery); | |||
| + } | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_soft_reset_parameter(struct fusb30x_chip *chip) | |||
| +{ | |||
| + chip->caps_counter = 0; | |||
| + chip->msg_id = 0; | |||
| + chip->vdm_state = VDM_STATE_DISCOVERY_ID; | |||
| + chip->vdm_substate = 0; | |||
| + chip->vdm_send_state = 0; | |||
| + chip->val_tmp = 0; | |||
| + chip->pos_power = 0; | |||
| +} | |||
| + | |||
| +static void fusb_state_src_startup(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + chip->notify.is_pd_connected = false; | |||
| + fusb_soft_reset_parameter(chip); | |||
| + | |||
| + memset(chip->partner_cap, 0, sizeof(chip->partner_cap)); | |||
| + | |||
| + tcpm_set_msg_header(chip); | |||
| + tcpm_set_polarity(chip, chip->cc_polarity); | |||
| + tcpm_set_rx_enable(chip, 1); | |||
| + | |||
| + set_state(chip, policy_src_send_caps); | |||
| + platform_fusb_notify(chip); | |||
| +} | |||
| + | |||
| +static void fusb_state_src_discovery(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + chip->caps_counter++; | |||
| + | |||
| + if (chip->caps_counter < N_CAPS_COUNT) { | |||
| + chip->timer_state = T_TYPEC_SEND_SOURCECAP; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + chip->sub_state = 1; | |||
| + } else { | |||
| + set_state(chip, disabled); | |||
| + } | |||
| + break; | |||
| + default: | |||
| + if (evt & EVENT_TIMER_STATE) { | |||
| + set_state(chip, policy_src_send_caps); | |||
| + } else if (evt & EVENT_TIMER_MUX) { | |||
| + if (!chip->is_pd_support) | |||
| + set_state(chip, disabled); | |||
| + else if (chip->hardrst_count > N_HARDRESET_COUNT) | |||
| + set_state(chip, error_recovery); | |||
| + else | |||
| + set_state(chip, policy_src_send_hardrst); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_src_send_caps(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, DMT_SOURCECAPABILITIES, DATAMESSAGE); | |||
| + chip->sub_state = 1; | |||
| + chip->tx_state = tx_idle; | |||
| + /* without break */ | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + | |||
| + if (tmp == tx_success) { | |||
| + chip->hardrst_count = 0; | |||
| + chip->caps_counter = 0; | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + chip->timer_mux = T_DISABLED; | |||
| + chip->sub_state++; | |||
| + chip->is_pd_support = true; | |||
| + } else if (tmp == tx_failed) { | |||
| + set_state(chip, policy_src_discovery); | |||
| + break; | |||
| + } | |||
| + | |||
| + if (!(evt & FLAG_EVENT)) | |||
| + break; | |||
| + default: | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_REQUEST)) { | |||
| + set_state(chip, policy_src_negotiate_cap); | |||
| + } else { | |||
| + set_state(chip, policy_src_send_softrst); | |||
| + } | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + if (chip->hardrst_count <= N_HARDRESET_COUNT) | |||
| + set_state(chip, policy_src_send_hardrst); | |||
| + else | |||
| + set_state(chip, disabled); | |||
| + } else if (evt & EVENT_TIMER_MUX) { | |||
| + if (!chip->is_pd_support) | |||
| + set_state(chip, disabled); | |||
| + else if (chip->hardrst_count > N_HARDRESET_COUNT) | |||
| + set_state(chip, error_recovery); | |||
| + else | |||
| + set_state(chip, policy_src_send_hardrst); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_src_negotiate_cap(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + /* base on evb1 */ | |||
| + tmp = (chip->rec_load[0] >> 28) & 0x07; | |||
| + if (tmp > chip->n_caps_used) | |||
| + set_state(chip, policy_src_cap_response); | |||
| + else | |||
| + set_state(chip, policy_src_transition_supply); | |||
| +} | |||
| + | |||
| +static void fusb_state_src_transition_supply(struct fusb30x_chip *chip, | |||
| + u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + /* without break */ | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->timer_state = T_SRC_TRANSITION; | |||
| + chip->sub_state++; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (tmp == tx_failed) { | |||
| + set_state(chip, policy_src_send_softrst); | |||
| + } | |||
| + break; | |||
| + case 2: | |||
| + if (evt & EVENT_TIMER_STATE) { | |||
| + chip->notify.is_pd_connected = true; | |||
| + platform_set_vbus_lvl_enable(chip, 1, 0); | |||
| + set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + } | |||
| + break; | |||
| + default: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + dev_info(chip->dev, | |||
| + "PD connected as DFP, supporting 5V\n"); | |||
| + set_state(chip, policy_src_ready); | |||
| + } else if (tmp == tx_failed) { | |||
| + set_state(chip, policy_src_send_softrst); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_src_cap_response(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, CMT_REJECT, CONTROLMESSAGE); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + /* without break */ | |||
| + default: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + if (chip->notify.is_pd_connected) { | |||
| + dev_info(chip->dev, | |||
| + "PD connected as DFP, supporting 5V\n"); | |||
| + set_state(chip, policy_src_ready); | |||
| + } else { | |||
| + set_state(chip, policy_src_send_hardrst); | |||
| + } | |||
| + } else if (tmp == tx_failed) { | |||
| + set_state(chip, policy_src_send_softrst); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_src_transition_default(struct fusb30x_chip *chip, | |||
| + u32 evt) | |||
| +{ | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + chip->notify.is_pd_connected = false; | |||
| + platform_set_vbus_lvl_enable(chip, 0, 0); | |||
| + if (chip->notify.data_role) | |||
| + regmap_update_bits(chip->regmap, | |||
| + FUSB_REG_SWITCHES1, | |||
| + SWITCHES1_DATAROLE, | |||
| + SWITCHES1_DATAROLE); | |||
| + else | |||
| + regmap_update_bits(chip->regmap, | |||
| + FUSB_REG_SWITCHES1, | |||
| + SWITCHES1_DATAROLE, | |||
| + 0); | |||
| + | |||
| + chip->timer_state = T_SRC_RECOVER; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + chip->sub_state++; | |||
| + break; | |||
| + default: | |||
| + if (evt & EVENT_TIMER_STATE) { | |||
| + platform_set_vbus_lvl_enable(chip, 1, 0); | |||
| + chip->timer_mux = T_NO_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_mux_machine, | |||
| + chip->timer_mux); | |||
| + set_state(chip, policy_src_startup); | |||
| + dev_dbg(chip->dev, "reset over-> src startup\n"); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_vcs_ufp_evaluate_swap(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + if (chip->vconn_supported) | |||
| + set_state(chip, policy_vcs_ufp_accept); | |||
| + else | |||
| + set_state(chip, policy_vcs_ufp_reject); | |||
| +} | |||
| + | |||
| +static void fusb_state_swap_msg_process(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PR_SWAP)) { | |||
| + set_state(chip, policy_src_prs_evaluate); | |||
| + } else if (PACKET_IS_CONTROL_MSG(chip->rec_head, | |||
| + CMT_VCONN_SWAP)) { | |||
| + if (chip->notify.data_role) | |||
| + set_state(chip, chip->conn_state); | |||
| + else | |||
| + set_state(chip, policy_vcs_ufp_evaluate_swap); | |||
| + } else if (PACKET_IS_CONTROL_MSG(chip->rec_head, | |||
| + CMT_DR_SWAP)) { | |||
| + if (chip->notify.data_role) | |||
| + set_state(chip, policy_drs_dfp_evaluate); | |||
| + else | |||
| + set_state(chip, policy_drs_ufp_evaluate); | |||
| + } | |||
| + } | |||
| +} | |||
| + | |||
| +#define VDM_IS_ACTIVE(chip) \ | |||
| + (chip->notify.data_role && chip->vdm_state < VDM_STATE_READY) | |||
| + | |||
| +static void fusb_state_src_ready(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_VENDERDEFINED)) { | |||
| + process_vdm_msg(chip); | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + chip->timer_state = T_DISABLED; | |||
| + } else if (!VDM_IS_ACTIVE(chip)) { | |||
| + fusb_state_swap_msg_process(chip, evt); | |||
| + } | |||
| + } | |||
| + | |||
| + if (!chip->partner_cap[0]) | |||
| + set_state(chip, policy_src_get_sink_caps); | |||
| + else if (VDM_IS_ACTIVE(chip)) | |||
| + auto_vdm_machine(chip, evt); | |||
| +} | |||
| + | |||
| +static void fusb_state_prs_evaluate(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + if (chip->role == ROLE_MODE_DRP) | |||
| + set_state(chip, policy_src_prs_accept); | |||
| + else | |||
| + set_state(chip, policy_src_prs_reject); | |||
| +} | |||
| + | |||
| +static void fusb_state_send_simple_msg(struct fusb30x_chip *chip, u32 evt, | |||
| + int cmd, int is_DMT, | |||
| + enum connection_state state_success, | |||
| + enum connection_state state_failed) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, cmd, is_DMT); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + /* fallthrough */ | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) | |||
| + set_state(chip, state_success); | |||
| + else if (tmp == tx_failed) | |||
| + set_state(chip, state_failed); | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_prs_reject(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + fusb_state_send_simple_msg(chip, evt, CMT_REJECT, CONTROLMESSAGE, | |||
| + (chip->notify.power_role) ? | |||
| + policy_src_ready : policy_snk_ready, | |||
| + (chip->notify.power_role) ? | |||
| + policy_src_send_softrst : | |||
| + policy_snk_send_softrst); | |||
| +} | |||
| + | |||
| +static void fusb_state_prs_accept(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE, | |||
| + (chip->notify.power_role) ? | |||
| + policy_src_prs_transition_to_off : | |||
| + policy_snk_prs_transition_to_off, | |||
| + (chip->notify.power_role) ? | |||
| + policy_src_send_softrst : | |||
| + policy_snk_send_softrst); | |||
| +} | |||
| + | |||
| +static void fusb_state_vcs_ufp_accept(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE, | |||
| + (chip->vconn_enabled) ? | |||
| + policy_vcs_ufp_wait_for_dfp_vconn : | |||
| + policy_vcs_ufp_turn_on_vconn, | |||
| + (chip->notify.power_role) ? | |||
| + policy_src_send_softrst : | |||
| + policy_snk_send_softrst); | |||
| +} | |||
| + | |||
| +static void fusb_state_vcs_set_vconn(struct fusb30x_chip *chip, | |||
| + u32 evt, bool on) | |||
| +{ | |||
| + if (on) { | |||
| + tcpm_set_vconn(chip, 1); | |||
| + set_state(chip, chip->notify.data_role ? | |||
| + policy_vcs_dfp_send_ps_rdy : | |||
| + policy_vcs_ufp_send_ps_rdy); | |||
| + } else { | |||
| + tcpm_set_vconn(chip, 0); | |||
| + if (chip->notify.power_role) | |||
| + set_state(chip, policy_src_ready); | |||
| + else | |||
| + set_state(chip, policy_snk_ready); | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_vcs_send_ps_rdy(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + fusb_state_send_simple_msg(chip, evt, CMT_PS_RDY, CONTROLMESSAGE, | |||
| + (chip->notify.power_role) ? | |||
| + policy_src_ready : policy_snk_ready, | |||
| + (chip->notify.power_role) ? | |||
| + policy_src_send_softrst : | |||
| + policy_snk_send_softrst); | |||
| +} | |||
| + | |||
| +static void fusb_state_vcs_wait_for_vconn(struct fusb30x_chip *chip, | |||
| + u32 evt) | |||
| +{ | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + chip->timer_state = T_PD_VCONN_SRC_ON; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + chip->sub_state++; | |||
| + /* fallthrough */ | |||
| + case 1: | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PS_RDY)) | |||
| + set_state(chip, chip->notify.data_role ? | |||
| + policy_vcs_dfp_turn_off_vconn : | |||
| + policy_vcs_ufp_turn_off_vconn); | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + if (chip->notify.power_role) | |||
| + set_state(chip, policy_src_send_hardrst); | |||
| + else | |||
| + set_state(chip, policy_snk_send_hardrst); | |||
| + } | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_src_prs_transition_to_off(struct fusb30x_chip *chip, | |||
| + u32 evt) | |||
| +{ | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + chip->timer_state = T_SRC_TRANSITION; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + chip->sub_state++; | |||
| + break; | |||
| + case 1: | |||
| + if (evt & EVENT_TIMER_STATE) { | |||
| + platform_set_vbus_lvl_enable(chip, 0, 0); | |||
| + chip->notify.power_role = POWER_ROLE_SINK; | |||
| + tcpm_set_msg_header(chip); | |||
| + if (chip->role == ROLE_MODE_DRP) | |||
| + set_state(chip, policy_src_prs_assert_rd); | |||
| + else | |||
| + set_state(chip, policy_src_prs_source_off); | |||
| + } | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_src_prs_assert_rd(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + tcpm_set_cc_pull_mode(chip, CC_PULL_DOWN); | |||
| + set_state(chip, policy_src_prs_source_off); | |||
| +} | |||
| + | |||
| +static void fusb_state_src_prs_source_off(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + /* fallthrough */ | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->timer_state = T_PD_SOURCE_ON; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + chip->sub_state++; | |||
| + } else if (tmp == tx_failed) { | |||
| + chip->notify.power_role = POWER_ROLE_SOURCE; | |||
| + tcpm_set_msg_header(chip); | |||
| + set_state(chip, policy_src_send_hardrst); | |||
| + } | |||
| + if (chip->sub_state != 3) | |||
| + break; | |||
| + case 2: | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_CONTROL_MSG(chip->rec_head, | |||
| + CMT_PS_RDY)) { | |||
| + chip->timer_state = T_DISABLED; | |||
| + /* snk startup */ | |||
| + chip->notify.is_pd_connected = false; | |||
| + chip->cc_state |= CC_STATE_TOGSS_IS_UFP; | |||
| + tcpm_set_polarity(chip, chip->cc_polarity); | |||
| + tcpm_set_rx_enable(chip, 1); | |||
| + set_state(chip, policy_snk_discovery); | |||
| + } else { | |||
| + dev_dbg(chip->dev, | |||
| + "rec careless msg: head %x\n", | |||
| + chip->rec_head); | |||
| + } | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + chip->notify.power_role = POWER_ROLE_SOURCE; | |||
| + tcpm_set_msg_header(chip); | |||
| + set_state(chip, policy_src_send_hardrst); | |||
| + } | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_drs_evaluate(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + if (chip->pd_cap_info.data_role_swap) | |||
| + /* | |||
| + * TODO: | |||
| + * NOW REJECT swap when the port is DFP | |||
| + * since we should work together with USB part | |||
| + */ | |||
| + set_state(chip, chip->notify.data_role ? | |||
| + policy_drs_dfp_reject : policy_drs_ufp_accept); | |||
| + else | |||
| + set_state(chip, chip->notify.data_role ? | |||
| + policy_drs_dfp_reject : policy_drs_ufp_reject); | |||
| +} | |||
| + | |||
| +static void fusb_state_drs_send_accept(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE, | |||
| + chip->notify.power_role ? | |||
| + policy_drs_dfp_change : | |||
| + policy_drs_ufp_change, | |||
| + error_recovery); | |||
| +} | |||
| + | |||
| +static void fusb_state_drs_role_change(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + chip->notify.data_role = chip->notify.data_role ? | |||
| + DATA_ROLE_UFP : DATA_ROLE_DFP; | |||
| + tcpm_set_msg_header(chip); | |||
| + set_state(chip, chip->notify.power_role ? policy_src_ready : | |||
| + policy_snk_ready); | |||
| +} | |||
| + | |||
| +static void fusb_state_src_get_sink_cap(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, CMT_GETSINKCAP, CONTROLMESSAGE); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + /* without break */ | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + chip->sub_state++; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (tmp == tx_failed) { | |||
| + set_state(chip, policy_src_send_softrst); | |||
| + } | |||
| + | |||
| + if (!(evt & FLAG_EVENT)) | |||
| + break; | |||
| + default: | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_DATA_MSG(chip->rec_head, | |||
| + DMT_SINKCAPABILITIES)) { | |||
| + for (tmp = 0; | |||
| + tmp < PD_HEADER_CNT(chip->rec_head); | |||
| + tmp++) { | |||
| + chip->partner_cap[tmp] = | |||
| + chip->rec_load[tmp]; | |||
| + } | |||
| + set_state(chip, policy_src_ready); | |||
| + } else { | |||
| + chip->partner_cap[0] = 0xffffffff; | |||
| + set_state(chip, policy_src_ready); | |||
| + } | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + dev_warn(chip->dev, "Get sink cap time out\n"); | |||
| + chip->partner_cap[0] = 0xffffffff; | |||
| + set_state(chip, policy_src_ready); | |||
| + } | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_src_send_hardreset(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + /* without break */ | |||
| + default: | |||
| + tmp = policy_send_hardrst(chip, evt); | |||
| + if (tmp == tx_success) { | |||
| + chip->hardrst_count++; | |||
| + set_state(chip, policy_src_transition_default); | |||
| + } else if (tmp == tx_failed) { | |||
| + /* can't reach here */ | |||
| + set_state(chip, error_recovery); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_src_softreset(struct fusb30x_chip *chip) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + /* without break */ | |||
| + default: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + fusb_soft_reset_parameter(chip); | |||
| + set_state(chip, policy_src_send_caps); | |||
| + } else if (tmp == tx_failed) { | |||
| + set_state(chip, policy_src_send_hardrst); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_src_send_softreset(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, CMT_SOFTRESET, CONTROLMESSAGE); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + /* without break */ | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + chip->sub_state++; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (tmp == tx_failed) { | |||
| + set_state(chip, policy_src_send_hardrst); | |||
| + } | |||
| + | |||
| + if (!(evt & FLAG_EVENT)) | |||
| + break; | |||
| + default: | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_ACCEPT)) { | |||
| + fusb_soft_reset_parameter(chip); | |||
| + set_state(chip, policy_src_send_caps); | |||
| + } | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + set_state(chip, policy_src_send_hardrst); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_startup(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + chip->notify.is_pd_connected = false; | |||
| + fusb_soft_reset_parameter(chip); | |||
| + | |||
| + memset(chip->partner_cap, 0, sizeof(chip->partner_cap)); | |||
| + | |||
| + tcpm_set_msg_header(chip); | |||
| + tcpm_set_polarity(chip, chip->cc_polarity); | |||
| + tcpm_set_rx_enable(chip, 1); | |||
| + set_state(chip, policy_snk_discovery); | |||
| + platform_fusb_notify(chip); | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_discovery(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + set_state(chip, policy_snk_wait_caps); | |||
| + chip->timer_state = T_TYPEC_SINK_WAIT_CAP; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_wait_caps(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_DATA_MSG(chip->rec_head, | |||
| + DMT_SOURCECAPABILITIES)) { | |||
| + chip->is_pd_support = true; | |||
| + chip->timer_mux = T_DISABLED; | |||
| + set_state(chip, policy_snk_evaluate_caps); | |||
| + } | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + if (chip->hardrst_count <= N_HARDRESET_COUNT) { | |||
| + if (chip->vbus_begin) { | |||
| + chip->vbus_begin = false; | |||
| + set_state(chip, policy_snk_send_softrst); | |||
| + } else { | |||
| + set_state(chip, policy_snk_send_hardrst); | |||
| + } | |||
| + } else { | |||
| + if (chip->is_pd_support) | |||
| + set_state(chip, error_recovery); | |||
| + else | |||
| + set_state(chip, disabled); | |||
| + } | |||
| + } else if ((evt & EVENT_TIMER_MUX) && | |||
| + (chip->hardrst_count > N_HARDRESET_COUNT)) { | |||
| + if (chip->is_pd_support) | |||
| + set_state(chip, error_recovery); | |||
| + else | |||
| + set_state(chip, disabled); | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_evaluate_caps(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + chip->hardrst_count = 0; | |||
| + chip->pos_power = 0; | |||
| + | |||
| + for (tmp = 0; tmp < PD_HEADER_CNT(chip->rec_head); tmp++) { | |||
| + switch (CAP_POWER_TYPE(chip->rec_load[tmp])) { | |||
| + case 0: | |||
| + /* Fixed Supply */ | |||
| + if (CAP_FPDO_VOLTAGE(chip->rec_load[tmp]) <= 100) | |||
| + chip->pos_power = tmp + 1; | |||
| + break; | |||
| + case 1: | |||
| + /* Battery */ | |||
| + if (CAP_VPDO_VOLTAGE(chip->rec_load[tmp]) <= 100) | |||
| + chip->pos_power = tmp + 1; | |||
| + break; | |||
| + default: | |||
| + /* not meet battery caps */ | |||
| + break; | |||
| + } | |||
| + } | |||
| + fusb302_set_pos_power_by_charge_ic(chip); | |||
| + | |||
| + if ((!chip->pos_power) || (chip->pos_power > 7)) { | |||
| + chip->pos_power = 0; | |||
| + set_state(chip, policy_snk_wait_caps); | |||
| + } else { | |||
| + set_state(chip, policy_snk_select_cap); | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_select_cap(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, DMT_REQUEST, DATAMESSAGE); | |||
| + chip->sub_state = 1; | |||
| + chip->tx_state = tx_idle; | |||
| + /* without break */ | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + | |||
| + if (tmp == tx_success) { | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + chip->sub_state++; | |||
| + } else if (tmp == tx_failed) { | |||
| + set_state(chip, policy_snk_discovery); | |||
| + break; | |||
| + } | |||
| + | |||
| + if (!(evt & FLAG_EVENT)) | |||
| + break; | |||
| + default: | |||
| + if (evt & EVENT_RX) { | |||
| + if (!PD_HEADER_CNT(chip->rec_head)) { | |||
| + switch (PD_HEADER_TYPE(chip->rec_head)) { | |||
| + case CMT_ACCEPT: | |||
| + set_state(chip, | |||
| + policy_snk_transition_sink); | |||
| + chip->timer_state = T_PS_TRANSITION; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + break; | |||
| + case CMT_WAIT: | |||
| + case CMT_REJECT: | |||
| + if (chip->notify.is_pd_connected) { | |||
| + dev_info(chip->dev, | |||
| + "PD connected as UFP, fetching 5V\n"); | |||
| + set_state(chip, | |||
| + policy_snk_ready); | |||
| + } else { | |||
| + set_state(chip, | |||
| + policy_snk_wait_caps); | |||
| + /* | |||
| + * make sure don't send | |||
| + * hard reset to prevent | |||
| + * infinite loop | |||
| + */ | |||
| + chip->hardrst_count = | |||
| + N_HARDRESET_COUNT + 1; | |||
| + } | |||
| + break; | |||
| + default: | |||
| + break; | |||
| + } | |||
| + } | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + set_state(chip, policy_snk_send_hardrst); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_transition_sink(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PS_RDY)) { | |||
| + chip->notify.is_pd_connected = true; | |||
| + dev_info(chip->dev, | |||
| + "PD connected as UFP, fetching 5V\n"); | |||
| + set_state(chip, policy_snk_ready); | |||
| + } else if (PACKET_IS_DATA_MSG(chip->rec_head, | |||
| + DMT_SOURCECAPABILITIES)) { | |||
| + set_state(chip, policy_snk_evaluate_caps); | |||
| + } | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + set_state(chip, policy_snk_send_hardrst); | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_transition_default(struct fusb30x_chip *chip, | |||
| + u32 evt) | |||
| +{ | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + chip->notify.is_pd_connected = false; | |||
| + chip->timer_mux = T_NO_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_mux_machine, | |||
| + chip->timer_mux); | |||
| + chip->timer_state = T_PS_HARD_RESET_MAX + T_SAFE_0V; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + if (chip->notify.data_role) | |||
| + tcpm_set_msg_header(chip); | |||
| + | |||
| + chip->sub_state++; | |||
| + /* fallthrough */ | |||
| + case 1: | |||
| + if (!tcpm_check_vbus(chip)) { | |||
| + chip->sub_state++; | |||
| + chip->timer_state = T_SRC_RECOVER_MAX + T_SRC_TURN_ON; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + set_state(chip, policy_snk_startup); | |||
| + } | |||
| + break; | |||
| + default: | |||
| + if (tcpm_check_vbus(chip)) { | |||
| + chip->timer_state = T_DISABLED; | |||
| + set_state(chip, policy_snk_startup); | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + set_state(chip, policy_snk_startup); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_ready(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_VENDERDEFINED)) { | |||
| + process_vdm_msg(chip); | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + chip->timer_state = T_DISABLED; | |||
| + } else if (!VDM_IS_ACTIVE(chip)) { | |||
| + fusb_state_swap_msg_process(chip, evt); | |||
| + } | |||
| + } | |||
| + | |||
| + if (VDM_IS_ACTIVE(chip)) | |||
| + auto_vdm_machine(chip, evt); | |||
| + | |||
| + fusb_state_swap_msg_process(chip, evt); | |||
| + platform_fusb_notify(chip); | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_send_hardreset(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + default: | |||
| + tmp = policy_send_hardrst(chip, evt); | |||
| + if (tmp == tx_success) { | |||
| + chip->hardrst_count++; | |||
| + set_state(chip, policy_snk_transition_default); | |||
| + } else if (tmp == tx_failed) { | |||
| + set_state(chip, error_recovery); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_send_swap(struct fusb30x_chip *chip, u32 evt, int cmd) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, cmd, CONTROLMESSAGE); | |||
| + chip->sub_state = 1; | |||
| + chip->tx_state = tx_idle; | |||
| + /* fallthrough */ | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + | |||
| + if (tmp == tx_success) { | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + chip->sub_state++; | |||
| + } else if (tmp == tx_failed) { | |||
| + if (cmd == CMT_DR_SWAP) { | |||
| + set_state(chip, error_recovery); | |||
| + return; | |||
| + } | |||
| + | |||
| + if (chip->notify.power_role) | |||
| + set_state(chip, policy_src_send_softrst); | |||
| + else | |||
| + set_state(chip, policy_snk_send_softrst); | |||
| + } | |||
| + break; | |||
| + case 2: | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_CONTROL_MSG(chip->rec_head, | |||
| + CMT_ACCEPT)) { | |||
| + chip->timer_state = T_DISABLED; | |||
| + if (cmd == CMT_VCONN_SWAP) { | |||
| + set_state(chip, chip->vconn_enabled ? | |||
| + policy_vcs_dfp_wait_for_ufp_vconn : | |||
| + policy_vcs_dfp_turn_on_vconn); | |||
| + } else if (cmd == CMT_PR_SWAP) { | |||
| + if (chip->notify.power_role) | |||
| + set_state(chip, policy_src_prs_transition_to_off); | |||
| + else | |||
| + set_state(chip, policy_snk_prs_transition_to_off); | |||
| + chip->notify.power_role = POWER_ROLE_SOURCE; | |||
| + tcpm_set_msg_header(chip); | |||
| + } else if (cmd == CMT_DR_SWAP) { | |||
| + set_state(chip, chip->notify.data_role ? | |||
| + policy_drs_dfp_change : | |||
| + policy_drs_ufp_change); | |||
| + } | |||
| + } else if (PACKET_IS_CONTROL_MSG(chip->rec_head, | |||
| + CMT_REJECT) || | |||
| + PACKET_IS_CONTROL_MSG(chip->rec_head, | |||
| + CMT_WAIT)) { | |||
| + chip->timer_state = T_DISABLED; | |||
| + if (chip->notify.power_role) | |||
| + set_state(chip, policy_src_ready); | |||
| + else | |||
| + set_state(chip, policy_snk_ready); | |||
| + } | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + if (chip->notify.power_role) | |||
| + set_state(chip, policy_src_ready); | |||
| + else | |||
| + set_state(chip, policy_snk_ready); | |||
| + } | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_prs_transition_to_off(struct fusb30x_chip *chip, | |||
| + u32 evt) | |||
| +{ | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + chip->timer_state = T_PD_SOURCE_OFF; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + chip->sub_state++; | |||
| + /* fallthrough */ | |||
| + case 1: | |||
| + if (evt & EVENT_RX) { | |||
| + if (PACKET_IS_CONTROL_MSG(chip->rec_head, | |||
| + CMT_PS_RDY)) { | |||
| + if (chip->role == ROLE_MODE_DRP) | |||
| + set_state(chip, | |||
| + policy_snk_prs_assert_rp); | |||
| + else | |||
| + set_state(chip, | |||
| + policy_snk_prs_source_on); | |||
| + } else { | |||
| + dev_dbg(chip->dev, | |||
| + "rec careless msg: head %x\n", | |||
| + chip->rec_head); | |||
| + } | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + chip->notify.power_role = POWER_ROLE_SINK; | |||
| + tcpm_set_msg_header(chip); | |||
| + set_state(chip, policy_snk_send_hardrst); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_prs_assert_rp(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + tcpm_set_cc_pull_mode(chip, CC_PULL_UP); | |||
| + set_state(chip, policy_snk_prs_source_on); | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_prs_source_on(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + /* supply power in 50ms */ | |||
| + platform_set_vbus_lvl_enable(chip, 1, 0); | |||
| + chip->sub_state++; | |||
| + chip->work_continue |= EVENT_WORK_CONTINUE; | |||
| + break; | |||
| + case 1: | |||
| + set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + /* fallthrough */ | |||
| + case 2: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + /* PD spe 6.5.10.2 */ | |||
| + chip->timer_state = T_PD_SWAP_SOURCE_START; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + chip->sub_state++; | |||
| + } else if (tmp == tx_failed) { | |||
| + chip->notify.power_role = POWER_ROLE_SINK; | |||
| + tcpm_set_msg_header(chip); | |||
| + set_state(chip, policy_snk_send_hardrst); | |||
| + } | |||
| + break; | |||
| + case 3: | |||
| + if (evt & EVENT_TIMER_STATE) { | |||
| + chip->cc_state &= ~CC_STATE_TOGSS_IS_UFP; | |||
| + regmap_update_bits(chip->regmap, FUSB_REG_MASK, | |||
| + MASK_M_COMP_CHNG, 0); | |||
| + set_state(chip, policy_src_send_caps); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_softreset(struct fusb30x_chip *chip) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + /* without break */ | |||
| + default: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + fusb_soft_reset_parameter(chip); | |||
| + chip->timer_state = T_TYPEC_SINK_WAIT_CAP; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + set_state(chip, policy_snk_wait_caps); | |||
| + } else if (tmp == tx_failed) { | |||
| + set_state(chip, policy_snk_send_hardrst); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_state_snk_send_softreset(struct fusb30x_chip *chip, u32 evt) | |||
| +{ | |||
| + u32 tmp; | |||
| + | |||
| + switch (chip->sub_state) { | |||
| + case 0: | |||
| + set_mesg(chip, CMT_SOFTRESET, CONTROLMESSAGE); | |||
| + chip->tx_state = tx_idle; | |||
| + chip->sub_state++; | |||
| + case 1: | |||
| + tmp = policy_send_data(chip); | |||
| + if (tmp == tx_success) { | |||
| + chip->timer_state = T_SENDER_RESPONSE; | |||
| + chip->sub_state++; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + } else if (tmp == tx_failed) { | |||
| + /* can't reach here */ | |||
| + set_state(chip, policy_snk_send_hardrst); | |||
| + } | |||
| + | |||
| + if (!(evt & FLAG_EVENT)) | |||
| + break; | |||
| + default: | |||
| + if (evt & EVENT_RX) { | |||
| + if ((!PD_HEADER_CNT(chip->rec_head)) && | |||
| + (PD_HEADER_TYPE(chip->rec_head) == CMT_ACCEPT)) { | |||
| + fusb_soft_reset_parameter(chip); | |||
| + chip->timer_state = T_TYPEC_SINK_WAIT_CAP; | |||
| + fusb_timer_start(&chip->timer_state_machine, | |||
| + chip->timer_state); | |||
| + set_state(chip, policy_snk_wait_caps); | |||
| + } | |||
| + } else if (evt & EVENT_TIMER_STATE) { | |||
| + set_state(chip, policy_snk_send_hardrst); | |||
| + } | |||
| + break; | |||
| + } | |||
| +} | |||
| + | |||
| +static void fusb_try_detach(struct fusb30x_chip *chip) | |||
| +{ | |||
| + int cc1, cc2; | |||
| + | |||
| + if ((chip->cc_state & CC_STATE_TOGSS_IS_UFP) && | |||
| + (chip->conn_state != | |||
| + policy_snk_transition_default) && | |||
| + (chip->conn_state != | |||
| + policy_src_prs_source_off) && | |||
| + (chip->conn_state != policy_snk_prs_send_swap) && | |||
| + (chip->conn_state != policy_snk_prs_assert_rp) && | |||
| + (chip->conn_state != policy_snk_prs_source_on) && | |||
| + (chip->conn_state != policy_snk_prs_transition_to_off)) { | |||
| + if (!tcpm_check_vbus(chip)) | |||
| + set_state_unattached(chip); | |||
| + } else if ((chip->conn_state != | |||
| + policy_src_transition_default) && | |||
| + (chip->conn_state != | |||
| + policy_src_prs_source_off) && | |||
| + (chip->conn_state != policy_snk_prs_source_on)) { | |||
| + tcpm_get_cc(chip, &cc1, &cc2); | |||
| + if (chip->cc_state & CC_STATE_TOGSS_CC2) | |||
| + cc1 = cc2; | |||
| + if (cc1 == TYPEC_CC_VOLT_OPEN) | |||
| + set_state_unattached(chip); | |||
| + } else { | |||
| + /* | |||
| + * Detached may occurred at swap operations. So, DON'T ignore | |||
| + * the EVENT_CC during swapping at all, check the connection | |||
| + * after it. | |||
| + */ | |||
| + chip->work_continue |= EVENT_DELAY_CC; | |||
| + } | |||
| +} | |||
| + | |||
| +static void state_machine_typec(struct fusb30x_chip *chip) | |||
| +{ | |||
| + u32 evt = 0; | |||
| + | |||
| + tcpc_alert(chip, &evt); | |||
| + mux_alert(chip, &evt); | |||
| + if (!evt) | |||
| + goto BACK; | |||
| + | |||
| + if (chip->notify.is_cc_connected) | |||
| + if (evt & (EVENT_CC | EVENT_DELAY_CC)) | |||
| + fusb_try_detach(chip); | |||
| + | |||
| + if (evt & EVENT_RX) { | |||
| + tcpm_get_message(chip); | |||
| + if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_SOFTRESET)) { | |||
| + if (chip->notify.power_role) | |||
| + set_state(chip, policy_src_softrst); | |||
| + else | |||
| + set_state(chip, policy_snk_softrst); | |||
| + } | |||
| + } | |||
| + | |||
| + if (evt & EVENT_TX) { | |||
| + if (chip->tx_state == tx_success) | |||
| + chip->msg_id++; | |||
| + } | |||
| + switch (chip->conn_state) { | |||
| + case disabled: | |||
| + fusb_state_disabled(chip, evt); | |||
| + break; | |||
| + case error_recovery: | |||
| + set_state_unattached(chip); | |||
| + break; | |||
| + case unattached: | |||
| + fusb_state_unattached(chip, evt); | |||
| + break; | |||
| + case attach_wait_sink: | |||
| + fusb_state_attach_wait_sink(chip, evt); | |||
| + break; | |||
| + case attach_wait_source: | |||
| + fusb_state_attach_wait_source(chip, evt); | |||
| + break; | |||
| + case attached_source: | |||
| + fusb_state_attached_source(chip, evt); | |||
| + break; | |||
| + case attached_sink: | |||
| + fusb_state_attached_sink(chip, evt); | |||
| + break; | |||
| + case attach_try_src: | |||
| + fusb_state_try_attach(chip, evt, ROLE_MODE_DFP); | |||
| + break; | |||
| + case attach_try_snk: | |||
| + fusb_state_try_attach(chip, evt, ROLE_MODE_UFP); | |||
| + break; | |||
| + | |||
| + /* POWER DELIVERY */ | |||
| + case policy_src_startup: | |||
| + fusb_state_src_startup(chip, evt); | |||
| + break; | |||
| + case policy_src_discovery: | |||
| + fusb_state_src_discovery(chip, evt); | |||
| + break; | |||
| + case policy_src_send_caps: | |||
| + fusb_state_src_send_caps(chip, evt); | |||
| + if (chip->conn_state != policy_src_negotiate_cap) | |||
| + break; | |||
| + case policy_src_negotiate_cap: | |||
| + fusb_state_src_negotiate_cap(chip, evt); | |||
| + | |||
| + case policy_src_transition_supply: | |||
| + fusb_state_src_transition_supply(chip, evt); | |||
| + break; | |||
| + case policy_src_cap_response: | |||
| + fusb_state_src_cap_response(chip, evt); | |||
| + break; | |||
| + case policy_src_transition_default: | |||
| + fusb_state_src_transition_default(chip, evt); | |||
| + break; | |||
| + case policy_src_ready: | |||
| + fusb_state_src_ready(chip, evt); | |||
| + break; | |||
| + case policy_src_get_sink_caps: | |||
| + fusb_state_src_get_sink_cap(chip, evt); | |||
| + break; | |||
| + case policy_src_send_hardrst: | |||
| + fusb_state_src_send_hardreset(chip, evt); | |||
| + break; | |||
| + case policy_src_send_softrst: | |||
| + fusb_state_src_send_softreset(chip, evt); | |||
| + break; | |||
| + case policy_src_softrst: | |||
| + fusb_state_src_softreset(chip); | |||
| + break; | |||
| + | |||
| + /* UFP */ | |||
| + case policy_snk_startup: | |||
| + fusb_state_snk_startup(chip, evt); | |||
| + break; | |||
| + case policy_snk_discovery: | |||
| + fusb_state_snk_discovery(chip, evt); | |||
| + break; | |||
| + case policy_snk_wait_caps: | |||
| + fusb_state_snk_wait_caps(chip, evt); | |||
| + break; | |||
| + case policy_snk_evaluate_caps: | |||
| + fusb_state_snk_evaluate_caps(chip, evt); | |||
| + /* without break */ | |||
| + case policy_snk_select_cap: | |||
| + fusb_state_snk_select_cap(chip, evt); | |||
| + break; | |||
| + case policy_snk_transition_sink: | |||
| + fusb_state_snk_transition_sink(chip, evt); | |||
| + break; | |||
| + case policy_snk_transition_default: | |||
| + fusb_state_snk_transition_default(chip, evt); | |||
| + break; | |||
| + case policy_snk_ready: | |||
| + fusb_state_snk_ready(chip, evt); | |||
| + break; | |||
| + case policy_snk_send_hardrst: | |||
| + fusb_state_snk_send_hardreset(chip, evt); | |||
| + break; | |||
| + case policy_snk_send_softrst: | |||
| + fusb_state_snk_send_softreset(chip, evt); | |||
| + break; | |||
| + case policy_snk_softrst: | |||
| + fusb_state_snk_softreset(chip); | |||
| + break; | |||
| + | |||
| + /* | |||
| + * PD Spec 1.0: PR SWAP: chap 8.3.3.6.3.1/2 | |||
| + * VC SWAP: chap 8.3.3.7.1/2 | |||
| + */ | |||
| + case policy_src_prs_evaluate: | |||
| + case policy_snk_prs_evaluate: | |||
| + fusb_state_prs_evaluate(chip, evt); | |||
| + break; | |||
| + case policy_snk_prs_accept: | |||
| + case policy_src_prs_accept: | |||
| + fusb_state_prs_accept(chip, evt); | |||
| + break; | |||
| + case policy_snk_prs_reject: | |||
| + case policy_src_prs_reject: | |||
| + case policy_vcs_ufp_reject: | |||
| + case policy_drs_dfp_reject: | |||
| + case policy_drs_ufp_reject: | |||
| + fusb_state_prs_reject(chip, evt); | |||
| + break; | |||
| + case policy_src_prs_transition_to_off: | |||
| + fusb_state_src_prs_transition_to_off(chip, evt); | |||
| + break; | |||
| + case policy_src_prs_assert_rd: | |||
| + fusb_state_src_prs_assert_rd(chip, evt); | |||
| + break; | |||
| + case policy_src_prs_source_off: | |||
| + fusb_state_src_prs_source_off(chip, evt); | |||
| + break; | |||
| + case policy_snk_prs_send_swap: | |||
| + case policy_src_prs_send_swap: | |||
| + fusb_state_send_swap(chip, evt, CMT_PR_SWAP); | |||
| + break; | |||
| + case policy_snk_prs_transition_to_off: | |||
| + fusb_state_snk_prs_transition_to_off(chip, evt); | |||
| + break; | |||
| + case policy_snk_prs_assert_rp: | |||
| + fusb_state_snk_prs_assert_rp(chip, evt); | |||
| + break; | |||
| + case policy_snk_prs_source_on: | |||
| + fusb_state_snk_prs_source_on(chip, evt); | |||
| + break; | |||
| + case policy_vcs_ufp_evaluate_swap: | |||
| + fusb_state_vcs_ufp_evaluate_swap(chip, evt); | |||
| + break; | |||
| + case policy_vcs_ufp_accept: | |||
| + fusb_state_vcs_ufp_accept(chip, evt); | |||
| + break; | |||
| + case policy_vcs_ufp_wait_for_dfp_vconn: | |||
| + case policy_vcs_dfp_wait_for_ufp_vconn: | |||
| + fusb_state_vcs_wait_for_vconn(chip, evt); | |||
| + break; | |||
| + case policy_vcs_ufp_turn_off_vconn: | |||
| + case policy_vcs_dfp_turn_off_vconn: | |||
| + fusb_state_vcs_set_vconn(chip, evt, false); | |||
| + break; | |||
| + case policy_vcs_ufp_turn_on_vconn: | |||
| + case policy_vcs_dfp_turn_on_vconn: | |||
| + fusb_state_vcs_set_vconn(chip, evt, true); | |||
| + break; | |||
| + case policy_vcs_ufp_send_ps_rdy: | |||
| + case policy_vcs_dfp_send_ps_rdy: | |||
| + fusb_state_vcs_send_ps_rdy(chip, evt); | |||
| + break; | |||
| + case policy_vcs_dfp_send_swap: | |||
| + fusb_state_send_swap(chip, evt, CMT_VCONN_SWAP); | |||
| + break; | |||
| + case policy_drs_ufp_evaluate: | |||
| + case policy_drs_dfp_evaluate: | |||
| + fusb_state_drs_evaluate(chip, evt); | |||
| + break; | |||
| + case policy_drs_dfp_accept: | |||
| + case policy_drs_ufp_accept: | |||
| + fusb_state_drs_send_accept(chip, evt); | |||
| + break; | |||
| + case policy_drs_dfp_change: | |||
| + case policy_drs_ufp_change: | |||
| + fusb_state_drs_role_change(chip, evt); | |||
| + break; | |||
| + case policy_drs_ufp_send_swap: | |||
| + case policy_drs_dfp_send_swap: | |||
| + fusb_state_send_swap(chip, evt, CMT_DR_SWAP); | |||
| + break; | |||
| + | |||
| + default: | |||
| + break; | |||
| + } | |||
| + | |||
| +BACK: | |||
| + if (chip->work_continue) { | |||
| + queue_work(chip->fusb30x_wq, &chip->work); | |||
| + return; | |||
| + } | |||
| + | |||
| + if (!platform_get_device_irq_state(chip)) | |||
| + fusb_irq_enable(chip); | |||
| + else | |||
| + queue_work(chip->fusb30x_wq, &chip->work); | |||
| +} | |||
| + | |||
| +static irqreturn_t cc_interrupt_handler(int irq, void *dev_id) | |||
| +{ | |||
| + struct fusb30x_chip *chip = dev_id; | |||
| + | |||
| + queue_work(chip->fusb30x_wq, &chip->work); | |||
| + fusb_irq_disable(chip); | |||
| + return IRQ_HANDLED; | |||
| +} | |||
| + | |||
| +static int fusb_initialize_gpio(struct fusb30x_chip *chip) | |||
| +{ | |||
| + chip->gpio_int = devm_gpiod_get_optional(chip->dev, "int-n", GPIOD_IN); | |||
| + if (IS_ERR(chip->gpio_int)) | |||
| + return PTR_ERR(chip->gpio_int); | |||
| + | |||
| + /* some board support vbus with other ways */ | |||
| + chip->gpio_vbus_5v = devm_gpiod_get_optional(chip->dev, "vbus-5v", | |||
| + GPIOD_OUT_LOW); | |||
| + if (IS_ERR(chip->gpio_vbus_5v)) | |||
| + dev_warn(chip->dev, | |||
| + "Could not get named GPIO for VBus5V!\n"); | |||
| + else | |||
| + gpiod_set_raw_value(chip->gpio_vbus_5v, 0); | |||
| + | |||
| + chip->gpio_vbus_other = devm_gpiod_get_optional(chip->dev, | |||
| + "vbus-other", | |||
| + GPIOD_OUT_LOW); | |||
| + if (IS_ERR(chip->gpio_vbus_other)) | |||
| + dev_warn(chip->dev, | |||
| + "Could not get named GPIO for VBusOther!\n"); | |||
| + else | |||
| + gpiod_set_raw_value(chip->gpio_vbus_other, 0); | |||
| + | |||
| + chip->gpio_discharge = devm_gpiod_get_optional(chip->dev, "discharge", | |||
| + GPIOD_OUT_LOW); | |||
| + if (IS_ERR(chip->gpio_discharge)) { | |||
| + dev_warn(chip->dev, | |||
| + "Could not get named GPIO for discharge!\n"); | |||
| + chip->gpio_discharge = NULL; | |||
| + } | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static enum hrtimer_restart fusb_timer_handler(struct hrtimer *timer) | |||
| +{ | |||
| + int i; | |||
| + | |||
| + for (i = 0; i < fusb30x_port_used; i++) { | |||
| + if (timer == &fusb30x_port_info[i]->timer_state_machine) { | |||
| + if (fusb30x_port_info[i]->timer_state != T_DISABLED) | |||
| + fusb30x_port_info[i]->timer_state = 0; | |||
| + break; | |||
| + } | |||
| + | |||
| + if (timer == &fusb30x_port_info[i]->timer_mux_machine) { | |||
| + if (fusb30x_port_info[i]->timer_mux != T_DISABLED) | |||
| + fusb30x_port_info[i]->timer_mux = 0; | |||
| + break; | |||
| + } | |||
| + } | |||
| + | |||
| + if (i != fusb30x_port_used) | |||
| + queue_work(fusb30x_port_info[i]->fusb30x_wq, | |||
| + &fusb30x_port_info[i]->work); | |||
| + | |||
| + return HRTIMER_NORESTART; | |||
| +} | |||
| + | |||
| +static void fusb_initialize_timer(struct fusb30x_chip *chip) | |||
| +{ | |||
| + hrtimer_init(&chip->timer_state_machine, CLOCK_MONOTONIC, | |||
| + HRTIMER_MODE_REL); | |||
| + chip->timer_state_machine.function = fusb_timer_handler; | |||
| + | |||
| + hrtimer_init(&chip->timer_mux_machine, CLOCK_MONOTONIC, | |||
| + HRTIMER_MODE_REL); | |||
| + chip->timer_mux_machine.function = fusb_timer_handler; | |||
| + | |||
| + chip->timer_state = T_DISABLED; | |||
| + chip->timer_mux = T_DISABLED; | |||
| +} | |||
| + | |||
| +static void fusb302_work_func(struct work_struct *work) | |||
| +{ | |||
| + struct fusb30x_chip *chip; | |||
| + | |||
| + chip = container_of(work, struct fusb30x_chip, work); | |||
| + state_machine_typec(chip); | |||
| +} | |||
| + | |||
| +static int fusb30x_probe(struct i2c_client *client, | |||
| + const struct i2c_device_id *id) | |||
| +{ | |||
| + struct fusb30x_chip *chip; | |||
| + struct PD_CAP_INFO *pd_cap_info; | |||
| + int ret; | |||
| + char *string[2]; | |||
| + | |||
| + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); | |||
| + if (!chip) | |||
| + return -ENOMEM; | |||
| + | |||
| + if (fusb30x_port_used == 0xff) | |||
| + return -1; | |||
| + | |||
| + chip->port_num = fusb30x_port_used++; | |||
| + fusb30x_port_info[chip->port_num] = chip; | |||
| + | |||
| + chip->dev = &client->dev; | |||
| + chip->regmap = devm_regmap_init_i2c(client, &fusb302_regmap_config); | |||
| + if (IS_ERR(chip->regmap)) { | |||
| + dev_err(&client->dev, "Failed to allocate regmap!\n"); | |||
| + return PTR_ERR(chip->regmap); | |||
| + } | |||
| + | |||
| + ret = fusb_initialize_gpio(chip); | |||
| + if (ret) | |||
| + return ret; | |||
| + | |||
| + fusb_initialize_timer(chip); | |||
| + | |||
| + chip->fusb30x_wq = create_workqueue("fusb302_wq"); | |||
| + INIT_WORK(&chip->work, fusb302_work_func); | |||
| + | |||
| + chip->role = ROLE_MODE_NONE; | |||
| + chip->try_role = ROLE_MODE_NONE; | |||
| + if (!of_property_read_string(chip->dev->of_node, "fusb302,role", | |||
| + (const char **)&string[0])) { | |||
| + if (!strcmp(string[0], "ROLE_MODE_DRP")) | |||
| + chip->role = ROLE_MODE_DRP; | |||
| + else if (!strcmp(string[0], "ROLE_MODE_DFP")) | |||
| + chip->role = ROLE_MODE_DFP; | |||
| + else if (!strcmp(string[0], "ROLE_MODE_UFP")) | |||
| + chip->role = ROLE_MODE_UFP; | |||
| + } | |||
| + | |||
| + if (chip->role == ROLE_MODE_NONE) { | |||
| + dev_warn(chip->dev, | |||
| + "Can't get property of role, set role to default DRP\n"); | |||
| + chip->role = ROLE_MODE_DRP; | |||
| + string[0] = "ROLE_MODE_DRP"; | |||
| + } | |||
| + | |||
| + if (!of_property_read_string(chip->dev->of_node, "fusb302,try_role", | |||
| + (const char **)&string[1])) { | |||
| + if (!strcmp(string[1], "ROLE_MODE_DFP")) | |||
| + chip->try_role = ROLE_MODE_DFP; | |||
| + else if (!strcmp(string[1], "ROLE_MODE_UFP")) | |||
| + chip->try_role = ROLE_MODE_UFP; | |||
| + } | |||
| + | |||
| + if (chip->try_role == ROLE_MODE_NONE) | |||
| + string[1] = "ROLE_MODE_NONE"; | |||
| + | |||
| + chip->vconn_supported = true; | |||
| + tcpm_init(chip); | |||
| + tcpm_set_rx_enable(chip, 0); | |||
| + chip->conn_state = unattached; | |||
| + tcpm_set_cc(chip, chip->role); | |||
| + | |||
| + chip->n_caps_used = 1; | |||
| + chip->source_power_supply[0] = 0x64; | |||
| + chip->source_max_current[0] = 0x96; | |||
| + | |||
| + pd_cap_info = &chip->pd_cap_info; | |||
| + pd_cap_info->dual_role_power = 1; | |||
| + pd_cap_info->data_role_swap = 1; | |||
| + | |||
| + pd_cap_info->externally_powered = 1; | |||
| + pd_cap_info->usb_suspend_support = 0; | |||
| + pd_cap_info->usb_communications_cap = 0; | |||
| + pd_cap_info->supply_type = 0; | |||
| + pd_cap_info->peak_current = 0; | |||
| + | |||
| + chip->extcon = devm_extcon_dev_allocate(&client->dev, fusb302_cable); | |||
| + if (IS_ERR(chip->extcon)) { | |||
| + dev_err(&client->dev, "allocat extcon failed\n"); | |||
| + return PTR_ERR(chip->extcon); | |||
| + } | |||
| + | |||
| + ret = devm_extcon_dev_register(&client->dev, chip->extcon); | |||
| + if (ret) { | |||
| + dev_err(&client->dev, "failed to register extcon: %d\n", | |||
| + ret); | |||
| + return ret; | |||
| + } | |||
| + | |||
| + ret = extcon_set_property_capability(chip->extcon, EXTCON_USB, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY); | |||
| + if (ret) { | |||
| + dev_err(&client->dev, | |||
| + "failed to set USB property capability: %d\n", | |||
| + ret); | |||
| + return ret; | |||
| + } | |||
| + | |||
| + ret = extcon_set_property_capability(chip->extcon, EXTCON_USB_HOST, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY); | |||
| + if (ret) { | |||
| + dev_err(&client->dev, | |||
| + "failed to set USB_HOST property capability: %d\n", | |||
| + ret); | |||
| + return ret; | |||
| + } | |||
| + | |||
| + ret = extcon_set_property_capability(chip->extcon, EXTCON_DISP_DP, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY); | |||
| + if (ret) { | |||
| + dev_err(&client->dev, | |||
| + "failed to set DISP_DP property capability: %d\n", | |||
| + ret); | |||
| + return ret; | |||
| + } | |||
| + | |||
| + ret = extcon_set_property_capability(chip->extcon, EXTCON_USB, | |||
| + EXTCON_PROP_USB_SS); | |||
| + if (ret) { | |||
| + dev_err(&client->dev, | |||
| + "failed to set USB USB_SS property capability: %d\n", | |||
| + ret); | |||
| + return ret; | |||
| + } | |||
| + | |||
| + ret = extcon_set_property_capability(chip->extcon, EXTCON_USB_HOST, | |||
| + EXTCON_PROP_USB_SS); | |||
| + if (ret) { | |||
| + dev_err(&client->dev, | |||
| + "failed to set USB_HOST USB_SS property capability: %d\n", | |||
| + ret); | |||
| + return ret; | |||
| + } | |||
| + | |||
| + ret = extcon_set_property_capability(chip->extcon, EXTCON_DISP_DP, | |||
| + EXTCON_PROP_USB_SS); | |||
| + if (ret) { | |||
| + dev_err(&client->dev, | |||
| + "failed to set DISP_DP USB_SS property capability: %d\n", | |||
| + ret); | |||
| + return ret; | |||
| + } | |||
| + | |||
| + ret = extcon_set_property_capability(chip->extcon, EXTCON_CHG_USB_FAST, | |||
| + EXTCON_PROP_USB_TYPEC_POLARITY); | |||
| + if (ret) { | |||
| + dev_err(&client->dev, | |||
| + "failed to set USB_PD property capability: %d\n", ret); | |||
| + return ret; | |||
| + } | |||
| + | |||
| + i2c_set_clientdata(client, chip); | |||
| + | |||
| + spin_lock_init(&chip->irq_lock); | |||
| + chip->enable_irq = 1; | |||
| + | |||
| + chip->gpio_int_irq = gpiod_to_irq(chip->gpio_int); | |||
| + if (chip->gpio_int_irq < 0) { | |||
| + dev_err(&client->dev, | |||
| + "Unable to request IRQ for INT_N GPIO! %d\n", | |||
| + ret); | |||
| + ret = chip->gpio_int_irq; | |||
| + goto IRQ_ERR; | |||
| + } | |||
| + | |||
| + ret = devm_request_threaded_irq(&client->dev, | |||
| + chip->gpio_int_irq, | |||
| + NULL, | |||
| + cc_interrupt_handler, | |||
| + IRQF_ONESHOT | IRQF_TRIGGER_LOW, | |||
| + client->name, | |||
| + chip); | |||
| + if (ret) { | |||
| + dev_err(&client->dev, "irq request failed\n"); | |||
| + goto IRQ_ERR; | |||
| + } | |||
| + | |||
| + dev_info(chip->dev, | |||
| + "port %d probe success with role %s, try_role %s\n", | |||
| + chip->port_num, string[0], string[1]); | |||
| + | |||
| + return 0; | |||
| + | |||
| +IRQ_ERR: | |||
| + destroy_workqueue(chip->fusb30x_wq); | |||
| + return ret; | |||
| +} | |||
| + | |||
| +static int fusb30x_remove(struct i2c_client *client) | |||
| +{ | |||
| + struct fusb30x_chip *chip = i2c_get_clientdata(client); | |||
| + | |||
| + destroy_workqueue(chip->fusb30x_wq); | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static void fusb30x_shutdown(struct i2c_client *client) | |||
| +{ | |||
| + struct fusb30x_chip *chip = i2c_get_clientdata(client); | |||
| + | |||
| + if (chip->gpio_vbus_5v) | |||
| + gpiod_set_value(chip->gpio_vbus_5v, 0); | |||
| + if (chip->gpio_discharge) { | |||
| + gpiod_set_value(chip->gpio_discharge, 1); | |||
| + msleep(100); | |||
| + gpiod_set_value(chip->gpio_discharge, 0); | |||
| + } | |||
| +} | |||
| + | |||
| +static const struct of_device_id fusb30x_dt_match[] = { | |||
| + { .compatible = FUSB30X_I2C_DEVICETREE_NAME }, | |||
| + {}, | |||
| +}; | |||
| +MODULE_DEVICE_TABLE(of, fusb30x_dt_match); | |||
| + | |||
| +static const struct i2c_device_id fusb30x_i2c_device_id[] = { | |||
| + { FUSB30X_I2C_DRIVER_NAME, 0 }, | |||
| + {} | |||
| +}; | |||
| +MODULE_DEVICE_TABLE(i2c, fusb30x_i2c_device_id); | |||
| + | |||
| +static struct i2c_driver fusb30x_driver = { | |||
| + .driver = { | |||
| + .name = FUSB30X_I2C_DRIVER_NAME, | |||
| + .of_match_table = of_match_ptr(fusb30x_dt_match), | |||
| + }, | |||
| + .probe = fusb30x_probe, | |||
| + .remove = fusb30x_remove, | |||
| + .shutdown = fusb30x_shutdown, | |||
| + .id_table = fusb30x_i2c_device_id, | |||
| +}; | |||
| + | |||
| +module_i2c_driver(fusb30x_driver); | |||
| + | |||
| +MODULE_LICENSE("GPL"); | |||
| +MODULE_AUTHOR("zain wang <zain.wang@rock-chips.com>"); | |||
| +MODULE_DESCRIPTION("fusb302 typec pd driver"); | |||
| diff --git a/drivers/staging/fusb30x/fusb30x.h b/drivers/staging/fusb30x/fusb30x.h | |||
| new file mode 100644 | |||
| index 000000000..4f5ca64f7 | |||
| --- /dev/null | |||
| +++ b/drivers/staging/fusb30x/fusb30x.h | |||
| @@ -0,0 +1,552 @@ | |||
| +/* | |||
| + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd | |||
| + * Author: Zain Wang <zain.wang@rock-chips.com> | |||
| + * | |||
| + * This program is free software; you can redistribute it and/or modify it | |||
| + * under the terms and conditions of the GNU General Public License, | |||
| + * version 2, as published by the Free Software Foundation. | |||
| + * | |||
| + * Some ideas are from chrome ec and fairchild GPL fusb302 driver. | |||
| + */ | |||
| + | |||
| +#ifndef FUSB302_H | |||
| +#define FUSB302_H | |||
| + | |||
| +#include <linux/i2c.h> | |||
| +#include <linux/hrtimer.h> | |||
| + | |||
| +const char *FUSB_DT_INTERRUPT_INTN = "fsc_interrupt_int_n"; | |||
| +#define FUSB_DT_GPIO_INTN "fairchild,int_n" | |||
| +#define FUSB_DT_GPIO_VBUS_5V "fairchild,vbus5v" | |||
| +#define FUSB_DT_GPIO_VBUS_OTHER "fairchild,vbusOther" | |||
| + | |||
| +#define FUSB30X_I2C_DRIVER_NAME "fusb302" | |||
| +#define FUSB30X_I2C_DEVICETREE_NAME "fairchild,fusb302" | |||
| + | |||
| +/* FUSB300 Register Addresses */ | |||
| +#define FUSB_REG_DEVICEID 0x01 | |||
| +#define FUSB_REG_SWITCHES0 0x02 | |||
| +#define FUSB_REG_SWITCHES1 0x03 | |||
| +#define FUSB_REG_MEASURE 0x04 | |||
| +#define FUSB_REG_SLICE 0x05 | |||
| +#define FUSB_REG_CONTROL0 0x06 | |||
| +#define FUSB_REG_CONTROL1 0x07 | |||
| +#define FUSB_REG_CONTROL2 0x08 | |||
| +#define FUSB_REG_CONTROL3 0x09 | |||
| +#define FUSB_REG_MASK 0x0A | |||
| +#define FUSB_REG_POWER 0x0B | |||
| +#define FUSB_REG_RESET 0x0C | |||
| +#define FUSB_REG_OCPREG 0x0D | |||
| +#define FUSB_REG_MASKA 0x0E | |||
| +#define FUSB_REG_MASKB 0x0F | |||
| +#define FUSB_REG_CONTROL4 0x10 | |||
| +#define FUSB_REG_STATUS0A 0x3C | |||
| +#define FUSB_REG_STATUS1A 0x3D | |||
| +#define FUSB_REG_INTERRUPTA 0x3E | |||
| +#define FUSB_REG_INTERRUPTB 0x3F | |||
| +#define FUSB_REG_STATUS0 0x40 | |||
| +#define FUSB_REG_STATUS1 0x41 | |||
| +#define FUSB_REG_INTERRUPT 0x42 | |||
| +#define FUSB_REG_FIFO 0x43 | |||
| + | |||
| +enum connection_state { | |||
| + disabled = 0, | |||
| + error_recovery, | |||
| + unattached, | |||
| + attach_wait_sink, | |||
| + attach_wait_source, | |||
| + attached_source, | |||
| + attached_sink, | |||
| + | |||
| + policy_src_startup, | |||
| + policy_src_send_caps, | |||
| + policy_src_discovery, | |||
| + policy_src_negotiate_cap, | |||
| + policy_src_cap_response, | |||
| + policy_src_transition_supply, | |||
| + policy_src_transition_default, | |||
| + | |||
| + policy_src_ready, | |||
| + policy_src_get_sink_caps, | |||
| + | |||
| + policy_src_send_softrst, | |||
| + policy_src_softrst, | |||
| + policy_src_send_hardrst, | |||
| + | |||
| + policy_snk_startup, | |||
| + policy_snk_discovery, | |||
| + policy_snk_wait_caps, | |||
| + policy_snk_evaluate_caps, | |||
| + policy_snk_select_cap, | |||
| + policy_snk_transition_sink, | |||
| + policy_snk_ready, | |||
| + | |||
| + policy_snk_send_softrst, | |||
| + policy_snk_softrst, | |||
| + policy_snk_send_hardrst, | |||
| + | |||
| + policy_snk_transition_default, | |||
| + | |||
| + /* PR SWAP */ | |||
| + policy_src_prs_evaluate, | |||
| + policy_src_prs_accept, | |||
| + policy_src_prs_transition_to_off, | |||
| + policy_src_prs_source_off, | |||
| + policy_src_prs_assert_rd, | |||
| + policy_src_prs_reject, | |||
| + policy_src_prs_send_swap, | |||
| + | |||
| + policy_snk_prs_evaluate, | |||
| + policy_snk_prs_accept, | |||
| + policy_snk_prs_transition_to_off, | |||
| + policy_snk_prs_source_on, | |||
| + policy_snk_prs_assert_rp, | |||
| + policy_snk_prs_reject, | |||
| + policy_snk_prs_send_swap, | |||
| + | |||
| + /* VC SWAP */ | |||
| + policy_vcs_dfp_send_swap, | |||
| + policy_vcs_dfp_wait_for_ufp_vconn, | |||
| + policy_vcs_dfp_turn_off_vconn, | |||
| + policy_vcs_dfp_turn_on_vconn, | |||
| + policy_vcs_dfp_send_ps_rdy, | |||
| + | |||
| + policy_vcs_ufp_evaluate_swap, | |||
| + policy_vcs_ufp_reject, | |||
| + policy_vcs_ufp_accept, | |||
| + policy_vcs_ufp_wait_for_dfp_vconn, | |||
| + policy_vcs_ufp_turn_off_vconn, | |||
| + policy_vcs_ufp_turn_on_vconn, | |||
| + policy_vcs_ufp_send_ps_rdy, | |||
| + | |||
| + policy_drs_ufp_evaluate, | |||
| + policy_drs_ufp_accept, | |||
| + policy_drs_ufp_reject, | |||
| + policy_drs_ufp_change, | |||
| + policy_drs_ufp_send_swap, | |||
| + | |||
| + policy_drs_dfp_evaluate, | |||
| + policy_drs_dfp_accept, | |||
| + policy_drs_dfp_reject, | |||
| + policy_drs_dfp_change, | |||
| + policy_drs_dfp_send_swap, | |||
| + | |||
| + attach_try_src, | |||
| + attach_try_snk, | |||
| +}; | |||
| + | |||
| +enum vdm_state { | |||
| + VDM_STATE_DISCOVERY_ID, | |||
| + VDM_STATE_DISCOVERY_SVID, | |||
| + VDM_STATE_DISCOVERY_MODES, | |||
| + VDM_STATE_ENTER_MODE, | |||
| + VDM_STATE_UPDATE_STATUS, | |||
| + VDM_STATE_DP_CONFIG, | |||
| + VDM_STATE_NOTIFY, | |||
| + VDM_STATE_READY, | |||
| + VDM_STATE_ERR, | |||
| +}; | |||
| + | |||
| +enum tcpm_rp_value { | |||
| + TYPEC_RP_USB = 0, | |||
| + TYPEC_RP_1A5 = 1, | |||
| + TYPEC_RP_3A0 = 2, | |||
| + TYPEC_RP_RESERVED = 3, | |||
| +}; | |||
| + | |||
| +enum role_mode { | |||
| + ROLE_MODE_NONE, | |||
| + ROLE_MODE_DRP, | |||
| + ROLE_MODE_UFP, | |||
| + ROLE_MODE_DFP, | |||
| + ROLE_MODE_ASS, | |||
| +}; | |||
| + | |||
| +#define SBF(s, v) ((s) << (v)) | |||
| +#define SWITCHES0_PDWN1 SBF(1, 0) | |||
| +#define SWITCHES0_PDWN2 SBF(1, 1) | |||
| +#define SWITCHES0_MEAS_CC1 SBF(1, 2) | |||
| +#define SWITCHES0_MEAS_CC2 SBF(1, 3) | |||
| +#define SWITCHES0_VCONN_CC1 SBF(1, 4) | |||
| +#define SWITCHES0_VCONN_CC2 SBF(1, 5) | |||
| +#define SWITCHES0_PU_EN1 SBF(1, 6) | |||
| +#define SWITCHES0_PU_EN2 SBF(1, 7) | |||
| + | |||
| +#define SWITCHES1_TXCC1 SBF(1, 0) | |||
| +#define SWITCHES1_TXCC2 SBF(1, 1) | |||
| +#define SWITCHES1_AUTO_CRC SBF(1, 2) | |||
| +#define SWITCHES1_DATAROLE SBF(1, 4) | |||
| +#define SWITCHES1_SPECREV SBF(3, 5) | |||
| +#define SWITCHES1_POWERROLE SBF(1, 7) | |||
| + | |||
| +#define MEASURE_MDAC SBF(0x3f, 0) | |||
| +#define MEASURE_VBUS SBF(1, 6) | |||
| + | |||
| +#define SLICE_SDAC SBF(0x3f, 0) | |||
| +#define SLICE_SDAC_HYS SBF(3, 6) | |||
| + | |||
| +#define CONTROL0_TX_START SBF(1, 0) | |||
| +#define CONTROL0_AUTO_PRE SBF(1, 1) | |||
| +#define CONTROL0_HOST_CUR SBF(3, 2) | |||
| +#define CONTROL0_HOST_CUR_USB SBF(1, 2) | |||
| +#define CONTROL0_HOST_CUR_1A5 SBF(2, 2) | |||
| +#define CONTROL0_HOST_CUR_3A0 SBF(3, 2) | |||
| +#define CONTROL0_INT_MASK SBF(1, 5) | |||
| +#define CONTROL0_TX_FLUSH SBF(1, 6) | |||
| + | |||
| +#define CONTROL1_ENSOP1 SBF(1, 0) | |||
| +#define CONTROL1_ENSOP2 SBF(1, 1) | |||
| +#define CONTROL1_RX_FLUSH SBF(1, 2) | |||
| +#define CONTROL1_BIST_MODE2 SBF(1, 4) | |||
| +#define CONTROL1_ENSOP1DB SBF(1, 5) | |||
| +#define CONTROL1_ENSOP2DB SBF(1, 6) | |||
| + | |||
| +#define CONTROL2_TOGGLE SBF(1, 0) | |||
| +#define CONTROL2_MODE SBF(3, 1) | |||
| +#define CONTROL2_MODE_NONE 0 | |||
| +#define CONTROL2_MODE_DFP SBF(3, 1) | |||
| +#define CONTROL2_MODE_UFP SBF(2, 1) | |||
| +#define CONTROL2_MODE_DRP SBF(1, 1) | |||
| +#define CONTROL2_WAKE_EN SBF(1, 3) | |||
| +#define CONTROL2_TOG_RD_ONLY SBF(1, 5) | |||
| +#define CONTROL2_TOG_SAVE_PWR1 SBF(1, 6) | |||
| +#define CONTROL2_TOG_SAVE_PWR2 SBF(1, 7) | |||
| + | |||
| +#define CONTROL3_AUTO_RETRY SBF(1, 0) | |||
| +#define CONTROL3_N_RETRIES SBF(3, 1) | |||
| +#define CONTROL3_AUTO_SOFTRESET SBF(1, 3) | |||
| +#define CONTROL3_AUTO_HARDRESET SBF(1, 4) | |||
| +#define CONTROL3_SEND_HARDRESET SBF(1, 6) | |||
| + | |||
| +#define MASK_M_BC_LVL SBF(1, 0) | |||
| +#define MASK_M_COLLISION SBF(1, 1) | |||
| +#define MASK_M_WAKE SBF(1, 2) | |||
| +#define MASK_M_ALERT SBF(1, 3) | |||
| +#define MASK_M_CRC_CHK SBF(1, 4) | |||
| +#define MASK_M_COMP_CHNG SBF(1, 5) | |||
| +#define MASK_M_ACTIVITY SBF(1, 6) | |||
| +#define MASK_M_VBUSOK SBF(1, 7) | |||
| + | |||
| +#define POWER_PWR SBF(0xf, 0) | |||
| + | |||
| +#define RESET_SW_RESET SBF(1, 0) | |||
| +#define RESET_PD_RESET SBF(1, 1) | |||
| + | |||
| +#define MASKA_M_HARDRST SBF(1, 0) | |||
| +#define MASKA_M_SOFTRST SBF(1, 1) | |||
| +#define MASKA_M_TXSENT SBF(1, 2) | |||
| +#define MASKA_M_HARDSENT SBF(1, 3) | |||
| +#define MASKA_M_RETRYFAIL SBF(1, 4) | |||
| +#define MASKA_M_SOFTFAIL SBF(1, 5) | |||
| +#define MASKA_M_TOGDONE SBF(1, 6) | |||
| +#define MASKA_M_OCP_TEMP SBF(1, 7) | |||
| + | |||
| +#define MASKB_M_GCRCSEND SBF(1, 0) | |||
| + | |||
| +#define CONTROL4_TOG_USRC_EXIT SBF(1, 0) | |||
| + | |||
| +#define MDAC_1P6V 0x26 | |||
| + | |||
| +#define STATUS0A_HARDRST SBF(1, 0) | |||
| +#define STATUS0A_SOFTRST SBF(1, 1) | |||
| +#define STATUS0A_POWER23 SBF(3, 2) | |||
| +#define STATUS0A_RETRYFAIL SBF(1, 4) | |||
| +#define STATUS0A_SOFTFAIL SBF(1, 5) | |||
| +#define STATUS0A_TOGDONE SBF(1, 6) | |||
| +#define STATUS0A_M_OCP_TEMP SBF(1, 7) | |||
| + | |||
| +#define STATUS1A_RXSOP SBF(1, 0) | |||
| +#define STATUS1A_RXSOP1DB SBF(1, 1) | |||
| +#define STATUS1A_RXSOP2DB SBF(1, 2) | |||
| +#define STATUS1A_TOGSS SBF(7, 3) | |||
| +#define CC_STATE_TOGSS_CC1 SBF(1, 0) | |||
| +#define CC_STATE_TOGSS_CC2 SBF(1, 1) | |||
| +#define CC_STATE_TOGSS_IS_UFP SBF(1, 2) | |||
| + | |||
| +#define INTERRUPTA_HARDRST SBF(1, 0) | |||
| +#define INTERRUPTA_SOFTRST SBF(1, 1) | |||
| +#define INTERRUPTA_TXSENT SBF(1, 2) | |||
| +#define INTERRUPTA_HARDSENT SBF(1, 3) | |||
| +#define INTERRUPTA_RETRYFAIL SBF(1, 4) | |||
| +#define INTERRUPTA_SOFTFAIL SBF(1, 5) | |||
| +#define INTERRUPTA_TOGDONE SBF(1, 6) | |||
| +#define INTERRUPTA_OCP_TEMP SBF(1, 7) | |||
| + | |||
| +#define INTERRUPTB_GCRCSENT SBF(1, 0) | |||
| + | |||
| +#define STATUS0_BC_LVL SBF(3, 0) | |||
| +#define STATUS0_WAKE SBF(1, 2) | |||
| +#define STATUS0_ALERT SBF(1, 3) | |||
| +#define STATUS0_CRC_CHK SBF(1, 4) | |||
| +#define STATUS0_COMP SBF(1, 5) | |||
| +#define STATUS0_ACTIVITY SBF(1, 6) | |||
| +#define STATUS0_VBUSOK SBF(1, 7) | |||
| + | |||
| +#define STATUS1_OCP SBF(1, 0) | |||
| +#define STATUS1_OVRTEMP SBF(1, 1) | |||
| +#define STATUS1_TX_FULL SBF(1, 2) | |||
| +#define STATUS1_TX_EMPTY SBF(1, 3) | |||
| +#define STATUS1_RX_FULL SBF(1, 4) | |||
| +#define STATUS1_RX_EMPTY SBF(1, 5) | |||
| +#define STATUS1_RXSOP1 SBF(1, 6) | |||
| +#define STATUS1_RXSOP2 SBF(1, 7) | |||
| + | |||
| +#define INTERRUPT_BC_LVL SBF(1, 0) | |||
| +#define INTERRUPT_COLLISION SBF(1, 1) | |||
| +#define INTERRUPT_WAKE SBF(1, 2) | |||
| +#define INTERRUPT_ALERT SBF(1, 3) | |||
| +#define INTERRUPT_CRC_CHK SBF(1, 4) | |||
| +#define INTERRUPT_COMP_CHNG SBF(1, 5) | |||
| +#define INTERRUPT_ACTIVITY SBF(1, 6) | |||
| +#define INTERRUPT_VBUSOK SBF(1, 7) | |||
| + | |||
| +#define FUSB_TKN_TXON 0xa1 | |||
| +#define FUSB_TKN_SYNC1 0x12 | |||
| +#define FUSB_TKN_SYNC2 0x13 | |||
| +#define FUSB_TKN_SYNC3 0x1b | |||
| +#define FUSB_TKN_RST1 0x15 | |||
| +#define FUSB_TKN_RST2 0x16 | |||
| +#define FUSB_TKN_PACKSYM 0x80 | |||
| +#define FUSB_TKN_JAMCRC 0xff | |||
| +#define FUSB_TKN_EOP 0x14 | |||
| +#define FUSB_TKN_TXOFF 0xfe | |||
| + | |||
| +/* USB PD Control Message Types */ | |||
| +#define CONTROLMESSAGE 0 | |||
| +#define CMT_GOODCRC 1 | |||
| +#define CMT_GOTOMIN 2 | |||
| +#define CMT_ACCEPT 3 | |||
| +#define CMT_REJECT 4 | |||
| +#define CMT_PING 5 | |||
| +#define CMT_PS_RDY 6 | |||
| +#define CMT_GETSOURCECAP 7 | |||
| +#define CMT_GETSINKCAP 8 | |||
| +#define CMT_DR_SWAP 9 | |||
| +#define CMT_PR_SWAP 10 | |||
| +#define CMT_VCONN_SWAP 11 | |||
| +#define CMT_WAIT 12 | |||
| +#define CMT_SOFTRESET 13 | |||
| + | |||
| +/* USB PD Data Message Types */ | |||
| +#define DATAMESSAGE 1 | |||
| +#define DMT_SOURCECAPABILITIES 1 | |||
| +#define DMT_REQUEST 2 | |||
| +#define DMT_BIST 3 | |||
| +#define DMT_SINKCAPABILITIES 4 | |||
| +#define DMT_VENDERDEFINED 15 | |||
| + | |||
| +/* VDM Command Types */ | |||
| +#define VDM_DISCOVERY_ID 0X01 | |||
| +#define VDM_DISCOVERY_SVIDS 0X02 | |||
| +#define VDM_DISCOVERY_MODES 0X03 | |||
| +#define VDM_ENTER_MODE 0X04 | |||
| +#define VDM_EXIT_MODE 0X05 | |||
| +#define VDM_ATTENTION 0X06 | |||
| +#define VDM_DP_STATUS_UPDATE 0X10 | |||
| +#define VDM_DP_CONFIG 0X11 | |||
| + | |||
| +#define VDM_TYPE_INIT 0 | |||
| +#define VDM_TYPE_ACK 1 | |||
| +#define VDM_TYPE_NACK 2 | |||
| +#define VDM_TYPE_BUSY 3 | |||
| + | |||
| +/* 200ms at least, 1 cycle about 6ms */ | |||
| +#define N_DEBOUNCE_CNT 33 | |||
| +#define N_CAPS_COUNT 50 | |||
| +#define N_HARDRESET_COUNT 0 | |||
| + | |||
| +#define T_NO_RESPONSE 5000 | |||
| +#define T_SRC_RECOVER 830 | |||
| +#define T_TYPEC_SEND_SOURCECAP 100 | |||
| +#define T_SENDER_RESPONSE 30 | |||
| +#define T_SRC_TRANSITION 30 | |||
| +#define T_TYPEC_SINK_WAIT_CAP 500 | |||
| +#define T_PS_TRANSITION 500 | |||
| +#define T_BMC_TIMEOUT 5 | |||
| +#define T_PS_HARD_RESET_MAX 35 | |||
| +#define T_SAFE_0V 650 | |||
| +#define T_SRC_TURN_ON 275 | |||
| +#define T_SRC_RECOVER_MAX 1000 | |||
| +#define T_PD_SOURCE_OFF 920 | |||
| +#define T_PD_SOURCE_ON 480 | |||
| +#define T_PD_SWAP_SOURCE_START 20 | |||
| +#define T_PD_VCONN_SRC_ON 100 | |||
| +#define T_PD_TRY_DRP 75 | |||
| + | |||
| +#define T_NO_TRIGGER 500 | |||
| +#define T_DISABLED 0xffff | |||
| + | |||
| +#define PD_HEADER_CNT(header) (((header) >> 12) & 7) | |||
| +#define PD_HEADER_TYPE(header) ((header) & 0xF) | |||
| +#define PD_HEADER_ID(header) (((header) >> 9) & 7) | |||
| + | |||
| +#define VDM_HEADER_TYPE(header) (((header) >> 6) & 3) | |||
| +#define VDMHEAD_CMD_TYPE_MASK (3 << 6) | |||
| +#define VDMHEAD_CMD_MASK (0x1f << 0) | |||
| +#define VDMHEAD_STRUCT_TYPE_MASK BIT(15) | |||
| + | |||
| +#define GET_VDMHEAD_CMD_TYPE(head) ((head & VDMHEAD_CMD_TYPE_MASK) >> 6) | |||
| +#define GET_VDMHEAD_CMD(head) (head & VDMHEAD_CMD_MASK) | |||
| +#define GET_VDMHEAD_STRUCT_TYPE(head) ((head & VDMHEAD_STRUCT_TYPE_MASK) >> 15) | |||
| + | |||
| +#define DP_STATUS_MASK 0x000000ff | |||
| +#define DP_STATUS_HPD_STATE BIT(7) | |||
| + | |||
| +#define GET_DP_STATUS(status) (status & DP_STATUS_MASK) | |||
| +#define GET_DP_STATUS_HPD(status) ((status & DP_STATUS_HPD_STATE) >> 7) | |||
| + | |||
| +#define VDM_IDHEAD_USBVID_MASK (0xffff << 0) | |||
| +#define VDM_IDHEAD_MODALSUPPORT_MASK BIT(26) | |||
| +#define VDM_IDHEAD_PRODUCTTYPE (7 << 27) | |||
| +#define VDM_IDHEAD_USBDEVICE BIT(30) | |||
| +#define VDM_IDHEAD_USBHOST BIT(30) | |||
| + | |||
| +#define CAP_POWER_TYPE(PDO) ((PDO >> 30) & 3) | |||
| +#define CAP_FPDO_VOLTAGE(PDO) ((PDO >> 10) & 0x3ff) | |||
| +#define CAP_VPDO_VOLTAGE(PDO) ((PDO >> 20) & 0x3ff) | |||
| +#define CAP_FPDO_CURRENT(PDO) ((PDO >> 0) & 0x3ff) | |||
| +#define CAP_VPDO_CURRENT(PDO) ((PDO >> 0) & 0x3ff) | |||
| + | |||
| +enum CC_ORIENTATION { | |||
| + NONE, | |||
| + CC1, | |||
| + CC2, | |||
| +}; | |||
| + | |||
| +enum typec_cc_polarity { | |||
| + TYPEC_POLARITY_CC1, | |||
| + TYPEC_POLARITY_CC2, | |||
| +}; | |||
| + | |||
| +enum CC_MODE { | |||
| + CC_PULL_UP, | |||
| + CC_PULL_DOWN, | |||
| + CC_PULL_NONE, | |||
| +}; | |||
| + | |||
| +enum typec_power_role { | |||
| + POWER_ROLE_SINK = 0, | |||
| + POWER_ROLE_SOURCE, | |||
| +}; | |||
| + | |||
| +enum typec_data_role { | |||
| + DATA_ROLE_UFP = 0, | |||
| + DATA_ROLE_DFP, | |||
| +}; | |||
| + | |||
| +struct notify_info { | |||
| + enum CC_ORIENTATION orientation; | |||
| + /* 0 UFP : 1 DFP */ | |||
| + enum typec_power_role power_role; | |||
| + enum typec_data_role data_role; | |||
| + | |||
| + bool is_cc_connected; | |||
| + bool is_pd_connected; | |||
| + | |||
| + bool is_enter_mode; | |||
| + int pin_assignment_support; | |||
| + int pin_assignment_def; | |||
| + bool attention; | |||
| + u32 dp_status; | |||
| + u32 dp_caps; | |||
| +}; | |||
| + | |||
| +enum tx_state { | |||
| + tx_idle, | |||
| + tx_busy, | |||
| + tx_failed, | |||
| + tx_success | |||
| +}; | |||
| + | |||
| +struct PD_CAP_INFO { | |||
| + u32 peak_current; | |||
| + u32 specification_revision; | |||
| + u32 externally_powered; | |||
| + u32 usb_suspend_support; | |||
| + u32 usb_communications_cap; | |||
| + u32 dual_role_power; | |||
| + u32 data_role_swap; | |||
| + u32 supply_type; | |||
| +}; | |||
| + | |||
| +struct fusb30x_chip { | |||
| + struct i2c_client *client; | |||
| + struct device *dev; | |||
| + struct regmap *regmap; | |||
| + struct work_struct work; | |||
| + struct workqueue_struct *fusb30x_wq; | |||
| + struct hrtimer timer_state_machine; | |||
| + struct hrtimer timer_mux_machine; | |||
| + struct PD_CAP_INFO pd_cap_info; | |||
| + struct notify_info notify; | |||
| + struct notify_info notify_cmp; | |||
| + struct extcon_dev *extcon; | |||
| + enum connection_state conn_state; | |||
| + struct gpio_desc *gpio_vbus_5v; | |||
| + struct gpio_desc *gpio_vbus_other; | |||
| + struct gpio_desc *gpio_int; | |||
| + struct gpio_desc *gpio_discharge; | |||
| + int timer_state; | |||
| + int timer_mux; | |||
| + int port_num; | |||
| + u32 work_continue; | |||
| + spinlock_t irq_lock; | |||
| + int gpio_int_irq; | |||
| + int enable_irq; | |||
| + | |||
| + /* | |||
| + * --------------------------------- | |||
| + * | role 0x03 << 2, | cc_use 0x03 | | |||
| + * | src 1 << 2, | cc1 1 | | |||
| + * | snk 2 << 2, | cc2 2 | | |||
| + * --------------------------------- | |||
| + */ | |||
| + u8 cc_state; | |||
| + int cc1; | |||
| + int cc2; | |||
| + enum typec_cc_polarity cc_polarity; | |||
| + u8 val_tmp; | |||
| + u8 debounce_cnt; | |||
| + int sub_state; | |||
| + int caps_counter; | |||
| + u32 send_load[7]; | |||
| + u32 rec_load[7]; | |||
| + u16 send_head; | |||
| + u16 rec_head; | |||
| + int msg_id; | |||
| + enum tx_state tx_state; | |||
| + int hardrst_count; | |||
| + u32 source_power_supply[7]; | |||
| + /* 50mv unit */ | |||
| + u32 source_max_current[7]; | |||
| + /* 10ma uint*/ | |||
| + int pos_power; | |||
| + /* | |||
| + * if PartnerCap[0] == 0xffffffff | |||
| + * show Partner Device do not support supply | |||
| + */ | |||
| + u32 partner_cap[7]; | |||
| + int n_caps_used; | |||
| + int vdm_state; | |||
| + int vdm_substate; | |||
| + int vdm_send_state; | |||
| + u16 vdm_svid[12]; | |||
| + int vdm_svid_num; | |||
| + u32 vdm_id; | |||
| + u8 chip_id; | |||
| + bool vconn_enabled; | |||
| + bool is_pd_support; | |||
| + int pd_output_vol; | |||
| + int pd_output_cur; | |||
| + int cc_meas_high; | |||
| + int cc_meas_low; | |||
| + bool vbus_begin; | |||
| + | |||
| + enum role_mode role; | |||
| + bool vconn_supported; | |||
| + bool try_role_complete; | |||
| + enum role_mode try_role; | |||
| +}; | |||
| + | |||
| +#endif /* FUSB302_H */ | |||
| + | |||
| @@ -0,0 +1,211 @@ | |||
| From 2fc2cbaaaf0dcebdeffa6e87bbc9ad843a5470dd Mon Sep 17 00:00:00 2001 | |||
| From: hmz007 <hmz007@gmail.com> | |||
| Date: Sat, 11 Jan 2020 19:35:03 +0800 | |||
| Subject: [PATCH] soc: friendlyelec: Add board info driver | |||
| Change-Id: I122adb4f99c816b5c177f16392fb2df9c10a47be | |||
| Signed-off-by: hmz007 <hmz007@gmail.com> | |||
| --- | |||
| drivers/soc/Kconfig | 1 + | |||
| drivers/soc/Makefile | 1 + | |||
| drivers/soc/friendlyelec/Kconfig | 11 ++ | |||
| drivers/soc/friendlyelec/Makefile | 1 + | |||
| drivers/soc/friendlyelec/board.c | 143 ++++++++++++++++++ | |||
| 8 files changed, 161 insertions(+) | |||
| create mode 100644 drivers/soc/friendlyelec/Kconfig | |||
| create mode 100644 drivers/soc/friendlyelec/Makefile | |||
| create mode 100644 drivers/soc/friendlyelec/board.c | |||
| diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig | |||
| index 833e04a7835c..9ddbd976395d 100644 | |||
| --- a/drivers/soc/Kconfig | |||
| +++ b/drivers/soc/Kconfig | |||
| @@ -21,5 +21,6 @@ source "drivers/soc/ux500/Kconfig" | |||
| source "drivers/soc/xilinx/Kconfig" | |||
| source "drivers/soc/zte/Kconfig" | |||
| source "drivers/soc/kendryte/Kconfig" | |||
| +source "drivers/soc/friendlyelec/Kconfig" | |||
| endmenu | |||
| diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile | |||
| index 2ec355003524..277b5bf269c7 100644 | |||
| --- a/drivers/soc/Makefile | |||
| +++ b/drivers/soc/Makefile | |||
| @@ -27,3 +27,4 @@ obj-$(CONFIG_ARCH_U8500) += ux500/ | |||
| obj-y += xilinx/ | |||
| obj-$(CONFIG_ARCH_ZX) += zte/ | |||
| obj-$(CONFIG_SOC_KENDRYTE) += kendryte/ | |||
| +obj-$(CONFIG_VENDOR_FRIENDLYELEC) += friendlyelec/ | |||
| diff --git a/drivers/soc/friendlyelec/Kconfig b/drivers/soc/friendlyelec/Kconfig | |||
| new file mode 100644 | |||
| index 000000000000..9e21c663e6c8 | |||
| --- /dev/null | |||
| +++ b/drivers/soc/friendlyelec/Kconfig | |||
| @@ -0,0 +1,11 @@ | |||
| +# | |||
| +# Machine drivers | |||
| +# | |||
| + | |||
| +if ARCH_ROCKCHIP | |||
| + | |||
| +config VENDOR_FRIENDLYELEC | |||
| + bool "FriendlyElec board based on RK33XX SoCs" | |||
| + default n | |||
| + | |||
| +endif | |||
| diff --git a/drivers/soc/friendlyelec/Makefile b/drivers/soc/friendlyelec/Makefile | |||
| new file mode 100644 | |||
| index 000000000000..870542f05177 | |||
| --- /dev/null | |||
| +++ b/drivers/soc/friendlyelec/Makefile | |||
| @@ -0,0 +1 @@ | |||
| +obj-$(CONFIG_VENDOR_FRIENDLYELEC) += board.o | |||
| diff --git a/drivers/soc/friendlyelec/board.c b/drivers/soc/friendlyelec/board.c | |||
| new file mode 100644 | |||
| index 000000000000..886a8e1f7dc0 | |||
| --- /dev/null | |||
| +++ b/drivers/soc/friendlyelec/board.c | |||
| @@ -0,0 +1,143 @@ | |||
| +/* | |||
| + * Copyright (C) Guangzhou FriendlyELEC Computer Tech. Co., Ltd. | |||
| + * (http://www.friendlyarm.com) | |||
| + * | |||
| + * 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, you can access it online at | |||
| + * http://www.gnu.org/licenses/gpl-2.0.html. | |||
| + */ | |||
| + | |||
| +#include <linux/kernel.h> | |||
| +#include <linux/module.h> | |||
| +#include <linux/init.h> | |||
| +#include <linux/types.h> | |||
| +#include <linux/io.h> | |||
| +#include <linux/of.h> | |||
| +#include <linux/of_platform.h> | |||
| +#include <linux/platform_device.h> | |||
| +#include <linux/slab.h> | |||
| +#include <linux/crc32.h> | |||
| +#include <linux/nvmem-consumer.h> | |||
| + | |||
| +#define BOARD_MANF "FriendlyELEC Computer Tech. Co., Ltd." | |||
| + | |||
| +static const char *board_mach; | |||
| +static const char *board_name; | |||
| +static u32 board_rev; | |||
| +static u32 board_serial_high, board_serial_low; | |||
| + | |||
| +static ssize_t board_sys_info_show(struct device *dev, | |||
| + struct device_attribute *attr, | |||
| + char *buf) | |||
| +{ | |||
| + char *s = buf; | |||
| + | |||
| + s += sprintf(s, "Hardware\t: %s\n", board_mach); | |||
| + s += sprintf(s, "Revision\t: %04x\n", board_rev); | |||
| + s += sprintf(s, "Serial\t\t: %08x%08x\n", | |||
| + board_serial_high, board_serial_low); | |||
| + s += sprintf(s, "\nModel\t\t: %s\n", board_name); | |||
| + s += sprintf(s, "Manufacturer\t: %s\n", BOARD_MANF); | |||
| + | |||
| + return (s - buf); | |||
| +} | |||
| + | |||
| +static struct device_attribute board_attr_info = | |||
| + __ATTR(info, S_IRUGO, board_sys_info_show, NULL); | |||
| + | |||
| +static int rockchip_cpuinfo_probe(struct platform_device *pdev) | |||
| +{ | |||
| + struct device *dev = &pdev->dev; | |||
| + struct nvmem_cell *cell; | |||
| + unsigned char *efuse_buf, buf[16]; | |||
| + size_t len; | |||
| + int i; | |||
| + | |||
| + cell = nvmem_cell_get(dev, "id"); | |||
| + if (IS_ERR(cell)) { | |||
| + dev_err(dev, "failed to get id cell: %ld\n", PTR_ERR(cell)); | |||
| + return PTR_ERR(cell); | |||
| + } | |||
| + | |||
| + efuse_buf = nvmem_cell_read(cell, &len); | |||
| + nvmem_cell_put(cell); | |||
| + | |||
| + if (len != 16) { | |||
| + kfree(efuse_buf); | |||
| + dev_err(dev, "invalid id len: %zu\n", len); | |||
| + return -EINVAL; | |||
| + } | |||
| + | |||
| + for (i = 0; i < 8; i++) { | |||
| + buf[i] = efuse_buf[1 + (i << 1)]; | |||
| + buf[i + 8] = efuse_buf[i << 1]; | |||
| + } | |||
| + | |||
| + kfree(efuse_buf); | |||
| + | |||
| + board_serial_low = crc32(0, buf, 8); | |||
| + board_serial_high = crc32(board_serial_low, buf + 8, 8); | |||
| + | |||
| + dev_info(dev, "Serial\t\t: %08x%08x\n", | |||
| + board_serial_high, board_serial_low); | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static int board_sys_probe(struct platform_device *pdev) | |||
| +{ | |||
| + struct device_node *np = pdev->dev.of_node; | |||
| + struct device_node *root; | |||
| + | |||
| + root = of_find_node_by_path("/"); | |||
| + | |||
| + of_property_read_u32(np, "hwrev", &board_rev); | |||
| + | |||
| + if (of_property_read_string(np, "machine", &board_mach)) | |||
| + of_property_read_string(root, "compatible", &board_mach); | |||
| + | |||
| + if (of_property_read_string(np, "model", &board_name)) | |||
| + of_property_read_string(root, "model", &board_name); | |||
| + | |||
| + of_node_put(root); | |||
| + | |||
| + rockchip_cpuinfo_probe(pdev); | |||
| + | |||
| + device_create_file(&pdev->dev, &board_attr_info); | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static const struct of_device_id board_sys_of_match[] = { | |||
| + { .compatible = "friendlyelec,board" }, | |||
| + {} | |||
| +}; | |||
| +MODULE_DEVICE_TABLE(of, board_sys_of_match); | |||
| + | |||
| +static struct platform_driver board_sys_driver = { | |||
| + .probe = board_sys_probe, | |||
| + .driver = { | |||
| + .name = "friendlyelec-board", | |||
| + .of_match_table = board_sys_of_match, | |||
| + }, | |||
| +}; | |||
| + | |||
| +static int __init board_sys_init(void) | |||
| +{ | |||
| + return platform_driver_register(&board_sys_driver); | |||
| +} | |||
| +late_initcall(board_sys_init); | |||
| + | |||
| +MODULE_AUTHOR("support@friendlyarm.com"); | |||
| +MODULE_DESCRIPTION("FriendlyElec NanoPi Series Machine Driver"); | |||
| +MODULE_LICENSE("GPL v2"); | |||
| @@ -0,0 +1,106 @@ | |||
| diff -u a/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi 2019-10-17 23:47:33.000000000 +0300 | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi 2019-10-27 22:34:55.988303874 +0300 | |||
| @@ -105,6 +105,27 @@ | |||
| }; | |||
| }; | |||
| + rt5651-sound { | |||
| + compatible = "simple-audio-card"; | |||
| + simple-audio-card,name = "realtek,rt5651-codec"; | |||
| + simple-audio-card,format = "i2s"; | |||
| + simple-audio-card,mclk-fs = <256>; | |||
| + simple-audio-card,widgets = | |||
| + "Microphone", "Mic Jack", | |||
| + "Headphone", "Headphone Jack"; | |||
| + simple-audio-card,routing = | |||
| + "Mic Jack", "micbias1", | |||
| + "IN1P", "Mic Jack", | |||
| + "Headphone Jack", "HPOL", | |||
| + "Headphone Jack", "HPOR"; | |||
| + simple-audio-card,cpu { | |||
| + sound-dai = <&i2s1>; | |||
| + }; | |||
| + simple-audio-card,codec { | |||
| + sound-dai = <&rt5651>; | |||
| + }; | |||
| + }; | |||
| + | |||
| sdio_pwrseq: sdio-pwrseq { | |||
| compatible = "mmc-pwrseq-simple"; | |||
| clocks = <&rk808 1>; | |||
| @@ -184,6 +205,10 @@ | |||
| status = "okay"; | |||
| }; | |||
| +&hdmi_sound { | |||
| + status = "okay"; | |||
| +}; | |||
| + | |||
| &i2c0 { | |||
| clock-frequency = <400000>; | |||
| i2c-scl-rising-time-ns = <160>; | |||
| @@ -432,6 +457,16 @@ | |||
| i2c-scl-rising-time-ns = <150>; | |||
| i2c-scl-falling-time-ns = <30>; | |||
| status = "okay"; | |||
| + | |||
| + rt5651: rt5651@1a { | |||
| + compatible = "realtek,rt5651"; | |||
| + reg = <0x1a>; | |||
| + clocks = <&cru SCLK_I2S_8CH_OUT>; | |||
| + clock-names = "mclk"; | |||
| + hp-det-gpio = <&gpio4 RK_PC4 GPIO_ACTIVE_LOW>; | |||
| + // spk-con-gpio = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; | |||
| + #sound-dai-cells = <0>; | |||
| + }; | |||
| }; | |||
| &i2c2 { | |||
| @@ -459,6 +494,16 @@ | |||
| status = "okay"; | |||
| }; | |||
| +&i2s1 { | |||
| + rockchip,playback-channels = <8>; | |||
| + rockchip,capture-channels = <8>; | |||
| + status = "okay"; | |||
| +}; | |||
| + | |||
| +&i2s2 { | |||
| + status = "okay"; | |||
| +}; | |||
| + | |||
| &io_domains { | |||
| bt656-supply = <&vcc_1v8>; | |||
| audio-supply = <&vcca1v8_codec>; | |||
| @@ -724,3 +769,9 @@ | |||
| &vopl_mmu { | |||
| status = "okay"; | |||
| }; | |||
| + | |||
| +&spdif { | |||
| + i2c-scl-rising-time-ns = <450>; | |||
| + i2c-scl-falling-time-ns = <15>; | |||
| + status = "okay"; | |||
| +}; | |||
| diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig | |||
| index b43657e6e..fb75c425e 100644 | |||
| --- a/sound/soc/rockchip/Kconfig | |||
| +++ b/sound/soc/rockchip/Kconfig | |||
| @@ -53,6 +53,15 @@ config SND_SOC_ROCKCHIP_RT5645 | |||
| Say Y or M here if you want to add support for SoC audio on Rockchip | |||
| boards using the RT5645/RT5650 codec, such as Veyron. | |||
| +config SND_SOC_ROCKCHIP_RT5651 | |||
| + tristate "ASoC support for Rockchip boards using a RT5651 codec" | |||
| + depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP | |||
| + select SND_SOC_ROCKCHIP_I2S | |||
| + select SND_SOC_RT5651 | |||
| + help | |||
| + Say Y or M here if you want to add support for SoC audio on Rockchip | |||
| + boards using the RT5651 codec, such as FriendlyARM's Nano{Pi,PC} family. | |||
| + | |||
| config SND_SOC_RK3288_HDMI_ANALOG | |||
| tristate "ASoC support multiple codecs for Rockchip RK3288 boards" | |||
| depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP | |||
| @@ -0,0 +1,15 @@ | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts | |||
| index 60358ab8c..057045ca3 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts | |||
| @@ -48,6 +48,10 @@ | |||
| }; | |||
| }; | |||
| +&gmac { | |||
| + rx_delay = <0x16>; | |||
| +}; | |||
| + | |||
| &vcc3v3_sys { | |||
| vin-supply = <&vcc5v0_core>; | |||
| }; | |||
| @@ -0,0 +1,13 @@ | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts | |||
| index 49050de8c..714616618 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi | |||
| @@ -646,7 +646,7 @@ | |||
| flash@0 { | |||
| compatible = "jedec,spi-nor"; | |||
| reg = <0>; | |||
| - spi-max-frequency = <10000000>; | |||
| + spi-max-frequency = <3000000>; | |||
| }; | |||
| }; | |||
| @@ -0,0 +1,82 @@ | |||
| diff --git a/arch/arm/boot/.gitignore b/arch/arm/boot/.gitignore | |||
| index 3c79f859..4e5c1d59 100644 | |||
| --- a/arch/arm/boot/.gitignore | |||
| +++ b/arch/arm/boot/.gitignore | |||
| @@ -3,3 +3,5 @@ zImage | |||
| xipImage | |||
| bootpImage | |||
| uImage | |||
| +*.dtb* | |||
| +*.scr | |||
| diff --git a/scripts/Makefile.dtbinst b/scripts/Makefile.dtbinst | |||
| index 50d580d77..94bd15617 100644 | |||
| --- a/scripts/Makefile.dtbinst | |||
| +++ b/scripts/Makefile.dtbinst | |||
| @@ -18,9 +18,12 @@ include scripts/Kbuild.include | |||
| include $(src)/Makefile | |||
| dtbs := $(addprefix $(dst)/, $(dtb-y) $(if $(CONFIG_OF_ALL_DTBS),$(dtb-))) | |||
| +dtbos := $(addprefix $(dst)/, $(dtbo-y)) | |||
| +scrs := $(addprefix $(dst)/, $(scr-y)) | |||
| +readmes := $(addprefix $(dst)/, $(dtbotxt-y)) | |||
| subdirs := $(addprefix $(obj)/, $(subdir-y) $(subdir-m)) | |||
| -__dtbs_install: $(dtbs) $(subdirs) | |||
| +__dtbs_install: $(dtbs) $(dtbos) $(scrs) $(readmes) $(subdirs) | |||
| @: | |||
| quiet_cmd_dtb_install = INSTALL $@ | |||
| @@ -29,6 +32,15 @@ quiet_cmd_dtb_install = INSTALL $@ | |||
| $(dst)/%.dtb: $(obj)/%.dtb | |||
| $(call cmd,dtb_install) | |||
| +$(dst)/%.dtbo: $(obj)/%.dtbo | |||
| + $(call cmd,dtb_install) | |||
| + | |||
| +$(dst)/%.scr: $(obj)/%.scr | |||
| + $(call cmd,dtb_install) | |||
| + | |||
| +$(dst)/README.rockchip-overlays: $(src)/README.rockchip-overlays | |||
| + $(call cmd,dtb_install) | |||
| + | |||
| PHONY += $(subdirs) | |||
| $(subdirs): | |||
| $(Q)$(MAKE) $(dtbinst)=$@ dst=$(patsubst $(obj)/%,$(dst)/%,$@) | |||
| diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib | |||
| index 58c05e5d..2b95dda9 100644 | |||
| --- a/scripts/Makefile.lib | |||
| +++ b/scripts/Makefile.lib | |||
| @@ -278,6 +278,9 @@ cmd_gzip = (cat $(filter-out FORCE,$^) | gzip -n -f -9 > $@) || \ | |||
| # --------------------------------------------------------------------------- | |||
| DTC ?= $(objtree)/scripts/dtc/dtc | |||
| +# Overlay support | |||
| +DTC_FLAGS += -@ -Wno-unit_address_format -Wno-simple_bus_reg | |||
| + | |||
| # Disable noisy checks by default | |||
| ifeq ($(KBUILD_ENABLE_EXTRA_GCC_CHECKS),) | |||
| DTC_FLAGS += -Wno-unit_address_vs_reg \ | |||
| @@ -324,6 +327,23 @@ cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \ | |||
| $(obj)/%.dtb: $(src)/%.dts FORCE | |||
| $(call if_changed_dep,dtc) | |||
| +quiet_cmd_dtco = DTCO $@ | |||
| +cmd_dtco = mkdir -p $(dir ${dtc-tmp}) ; \ | |||
| + $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \ | |||
| + $(DTC) -O dtb -o $@ -b 0 \ | |||
| + -i $(dir $<) $(DTC_FLAGS) \ | |||
| + -d $(depfile).dtc.tmp $(dtc-tmp) ; \ | |||
| + cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) | |||
| + | |||
| +$(obj)/%.dtbo: $(src)/%.dts FORCE | |||
| + $(call if_changed_dep,dtco) | |||
| + | |||
| +quiet_cmd_scr = MKIMAGE $@ | |||
| +cmd_scr = mkimage -C none -A $(ARCH) -T script -d $< $@ | |||
| + | |||
| +$(obj)/%.scr: $(src)/%.scr-cmd FORCE | |||
| + $(call if_changed,scr) | |||
| + | |||
| dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) | |||
| # Bzip2 | |||
| @@ -0,0 +1,430 @@ | |||
| Below patch is a squash of following 4 commits borrowed from ayufan's | |||
| https://github.com/ayufan-rock64/linux-mainline-kernel repo: | |||
| From cc22206776d61948f6984a4f03d8013eb4f92984 Mon Sep 17 00:00:00 2001 | |||
| From: Pantelis Antoniou <pantelis.antoniou@konsulko.com> | |||
| Date: Wed, 3 Dec 2014 13:23:28 +0200 | |||
| Subject: [PATCH] OF: DT-Overlay configfs interface | |||
| This is a port of Pantelis Antoniou's v3 port that makes use of the | |||
| new upstreamed configfs support for binary attributes. | |||
| Original commit message: | |||
| Add a runtime interface to using configfs for generic device tree overlay | |||
| usage. With it its possible to use device tree overlays without having | |||
| to use a per-platform overlay manager. | |||
| Please see Documentation/devicetree/configfs-overlays.txt for more info. | |||
| Changes since v2: | |||
| - Removed ifdef CONFIG_OF_OVERLAY (since for now it's required) | |||
| - Created a documentation entry | |||
| - Slight rewording in Kconfig | |||
| Changes since v1: | |||
| - of_resolve() -> of_resolve_phandles(). | |||
| Originally-signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> | |||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | |||
| DT configfs: Fix build errors on other platforms | |||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | |||
| DT configfs: fix build error | |||
| There is an error when compiling rpi-4.6.y branch: | |||
| CC drivers/of/configfs.o | |||
| drivers/of/configfs.c:291:21: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] | |||
| .default_groups = of_cfs_def_groups, | |||
| ^ | |||
| drivers/of/configfs.c:291:21: note: (near initialization for 'of_cfs_subsys.su_group.default_groups.next') | |||
| The .default_groups is linked list since commit | |||
| 1ae1602de028acaa42a0f6ff18d19756f8e825c6. | |||
| This commit uses configfs_add_default_group to fix this problem. | |||
| Change-Id: Id4903c9725ebc355aadd067bbc32047db622ce61 | |||
| Signed-off-by: Slawomir Stepien <sst@poczta.fm> | |||
| From 7cfb967c52d374ddcb0fc9194f38d1d5f9d8cdd8 Mon Sep 17 00:00:00 2001 | |||
| From: popcornmix <popcornmix@gmail.com> | |||
| Date: Sun, 3 Dec 2017 21:43:03 +0000 | |||
| Subject: [PATCH] configfs: New of_overlay API | |||
| From 8637321fabb045fe8617360ef1b058978b0d8457 Mon Sep 17 00:00:00 2001 | |||
| From: Phil Elwell <phil@raspberrypi.org> | |||
| Date: Mon, 4 Dec 2017 14:07:40 +0000 | |||
| Subject: [PATCH] SQUASH: config_fs: of_overlay_apply takes a pointer | |||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | |||
| From 274dfabb947ca32116a429c582c74aaee6ff1b5b Mon Sep 17 00:00:00 2001 | |||
| From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= <ayufan@ayufan.eu> | |||
| Date: Mon, 14 May 2018 11:28:09 +0200 | |||
| Subject: [PATCH] ayufan: fix overlayfs to compile on 4.17 kernel | |||
| Change-Id: I421887697d2ba6e52aba9100100b7664760e2001 | |||
| diff --git a/Documentation/devicetree/configfs-overlays.txt b/Documentation/devicetree/configfs-overlays.txt | |||
| new file mode 100644 | |||
| index 0000000000000..5fa43e0643072 | |||
| --- /dev/null | |||
| +++ b/Documentation/devicetree/configfs-overlays.txt | |||
| @@ -0,0 +1,31 @@ | |||
| +Howto use the configfs overlay interface. | |||
| + | |||
| +A device-tree configfs entry is created in /config/device-tree/overlays | |||
| +and and it is manipulated using standard file system I/O. | |||
| +Note that this is a debug level interface, for use by developers and | |||
| +not necessarily something accessed by normal users due to the | |||
| +security implications of having direct access to the kernel's device tree. | |||
| + | |||
| +* To create an overlay you mkdir the directory: | |||
| + | |||
| + # mkdir /config/device-tree/overlays/foo | |||
| + | |||
| +* Either you echo the overlay firmware file to the path property file. | |||
| + | |||
| + # echo foo.dtbo >/config/device-tree/overlays/foo/path | |||
| + | |||
| +* Or you cat the contents of the overlay to the dtbo file | |||
| + | |||
| + # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo | |||
| + | |||
| +The overlay file will be applied, and devices will be created/destroyed | |||
| +as required. | |||
| + | |||
| +To remove it simply rmdir the directory. | |||
| + | |||
| + # rmdir /config/device-tree/overlays/foo | |||
| + | |||
| +The rationalle of the dual interface (firmware & direct copy) is that each is | |||
| +better suited to different use patterns. The firmware interface is what's | |||
| +intended to be used by hardware managers in the kernel, while the copy interface | |||
| +make sense for developers (since it avoids problems with namespaces). | |||
| diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig | |||
| index 37c2ccbefecdc..d3fc81a40c0e7 100644 | |||
| --- a/drivers/of/Kconfig | |||
| +++ b/drivers/of/Kconfig | |||
| @@ -103,4 +103,11 @@ config OF_OVERLAY | |||
| config OF_NUMA | |||
| bool | |||
| +config OF_CONFIGFS | |||
| + bool "Device Tree Overlay ConfigFS interface" | |||
| + select CONFIGFS_FS | |||
| + select OF_OVERLAY | |||
| + help | |||
| + Enable a simple user-space driven DT overlay interface. | |||
| + | |||
| endif # OF | |||
| diff --git a/drivers/of/Makefile b/drivers/of/Makefile | |||
| index 663a4af0cccd5..b00a95adf5199 100644 | |||
| --- a/drivers/of/Makefile | |||
| +++ b/drivers/of/Makefile | |||
| @@ -1,6 +1,7 @@ | |||
| # SPDX-License-Identifier: GPL-2.0 | |||
| obj-y = base.o device.o platform.o property.o | |||
| obj-$(CONFIG_OF_KOBJ) += kobj.o | |||
| +obj-$(CONFIG_OF_CONFIGFS) += configfs.o | |||
| obj-$(CONFIG_OF_DYNAMIC) += dynamic.o | |||
| obj-$(CONFIG_OF_FLATTREE) += fdt.o | |||
| obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o | |||
| diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c | |||
| new file mode 100644 | |||
| index 000000000..5dd509e8f | |||
| --- /dev/null | |||
| +++ b/drivers/of/configfs.c | |||
| @@ -0,0 +1,290 @@ | |||
| +/* | |||
| + * Configfs entries for device-tree | |||
| + * | |||
| + * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com> | |||
| + * | |||
| + * 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. | |||
| + */ | |||
| +#include <linux/ctype.h> | |||
| +#include <linux/cpu.h> | |||
| +#include <linux/module.h> | |||
| +#include <linux/of.h> | |||
| +#include <linux/of_fdt.h> | |||
| +#include <linux/spinlock.h> | |||
| +#include <linux/slab.h> | |||
| +#include <linux/proc_fs.h> | |||
| +#include <linux/configfs.h> | |||
| +#include <linux/types.h> | |||
| +#include <linux/stat.h> | |||
| +#include <linux/limits.h> | |||
| +#include <linux/file.h> | |||
| +#include <linux/vmalloc.h> | |||
| +#include <linux/firmware.h> | |||
| +#include <linux/sizes.h> | |||
| + | |||
| +#include "of_private.h" | |||
| + | |||
| +struct cfs_overlay_item { | |||
| + struct config_item item; | |||
| + | |||
| + char path[PATH_MAX]; | |||
| + | |||
| + const struct firmware *fw; | |||
| + struct device_node *overlay; | |||
| + int ov_id; | |||
| + | |||
| + void *dtbo; | |||
| + int dtbo_size; | |||
| +}; | |||
| + | |||
| +static int create_overlay(struct cfs_overlay_item *overlay, void *blob, u32 blob_size) | |||
| +{ | |||
| + int err; | |||
| + | |||
| + err = of_overlay_fdt_apply(blob, blob_size, &overlay->ov_id); | |||
| + if (err < 0) { | |||
| + pr_err("%s: Failed to create overlay (err=%d)\n", | |||
| + __func__, err); | |||
| + goto out_err; | |||
| + } | |||
| + | |||
| +out_err: | |||
| + return err; | |||
| +} | |||
| + | |||
| +static inline struct cfs_overlay_item *to_cfs_overlay_item( | |||
| + struct config_item *item) | |||
| +{ | |||
| + return item ? container_of(item, struct cfs_overlay_item, item) : NULL; | |||
| +} | |||
| + | |||
| +static ssize_t cfs_overlay_item_path_show(struct config_item *item, | |||
| + char *page) | |||
| +{ | |||
| + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); | |||
| + return sprintf(page, "%s\n", overlay->path); | |||
| +} | |||
| + | |||
| +static ssize_t cfs_overlay_item_path_store(struct config_item *item, | |||
| + const char *page, size_t count) | |||
| +{ | |||
| + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); | |||
| + const char *p = page; | |||
| + char *s; | |||
| + int err; | |||
| + | |||
| + /* if it's set do not allow changes */ | |||
| + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) | |||
| + return -EPERM; | |||
| + | |||
| + /* copy to path buffer (and make sure it's always zero terminated */ | |||
| + count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); | |||
| + overlay->path[sizeof(overlay->path) - 1] = '\0'; | |||
| + | |||
| + /* strip trailing newlines */ | |||
| + s = overlay->path + strlen(overlay->path); | |||
| + while (s > overlay->path && *--s == '\n') | |||
| + *s = '\0'; | |||
| + | |||
| + pr_debug("%s: path is '%s'\n", __func__, overlay->path); | |||
| + | |||
| + err = request_firmware(&overlay->fw, overlay->path, NULL); | |||
| + if (err != 0) | |||
| + goto out_err; | |||
| + | |||
| + err = create_overlay(overlay, (void *)overlay->fw->data, overlay->fw->size); | |||
| + if (err != 0) | |||
| + goto out_err; | |||
| + | |||
| + return count; | |||
| + | |||
| +out_err: | |||
| + | |||
| + release_firmware(overlay->fw); | |||
| + overlay->fw = NULL; | |||
| + | |||
| + overlay->path[0] = '\0'; | |||
| + return err; | |||
| +} | |||
| + | |||
| +static ssize_t cfs_overlay_item_status_show(struct config_item *item, | |||
| + char *page) | |||
| +{ | |||
| + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); | |||
| + | |||
| + return sprintf(page, "%s\n", | |||
| + overlay->ov_id >= 0 ? "applied" : "unapplied"); | |||
| +} | |||
| + | |||
| +CONFIGFS_ATTR(cfs_overlay_item_, path); | |||
| +CONFIGFS_ATTR_RO(cfs_overlay_item_, status); | |||
| + | |||
| +static struct configfs_attribute *cfs_overlay_attrs[] = { | |||
| + &cfs_overlay_item_attr_path, | |||
| + &cfs_overlay_item_attr_status, | |||
| + NULL, | |||
| +}; | |||
| + | |||
| +ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, | |||
| + void *buf, size_t max_count) | |||
| +{ | |||
| + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); | |||
| + | |||
| + pr_debug("%s: buf=%p max_count=%zu\n", __func__, | |||
| + buf, max_count); | |||
| + | |||
| + if (overlay->dtbo == NULL) | |||
| + return 0; | |||
| + | |||
| + /* copy if buffer provided */ | |||
| + if (buf != NULL) { | |||
| + /* the buffer must be large enough */ | |||
| + if (overlay->dtbo_size > max_count) | |||
| + return -ENOSPC; | |||
| + | |||
| + memcpy(buf, overlay->dtbo, overlay->dtbo_size); | |||
| + } | |||
| + | |||
| + return overlay->dtbo_size; | |||
| +} | |||
| + | |||
| +ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, | |||
| + const void *buf, size_t count) | |||
| +{ | |||
| + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); | |||
| + int err; | |||
| + | |||
| + /* if it's set do not allow changes */ | |||
| + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) | |||
| + return -EPERM; | |||
| + | |||
| + /* copy the contents */ | |||
| + overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); | |||
| + if (overlay->dtbo == NULL) | |||
| + return -ENOMEM; | |||
| + | |||
| + overlay->dtbo_size = count; | |||
| + | |||
| + err = create_overlay(overlay, overlay->dtbo, overlay->dtbo_size); | |||
| + if (err != 0) | |||
| + goto out_err; | |||
| + | |||
| + return count; | |||
| + | |||
| +out_err: | |||
| + kfree(overlay->dtbo); | |||
| + overlay->dtbo = NULL; | |||
| + overlay->dtbo_size = 0; | |||
| + | |||
| + return err; | |||
| +} | |||
| + | |||
| +CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); | |||
| + | |||
| +static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { | |||
| + &cfs_overlay_item_attr_dtbo, | |||
| + NULL, | |||
| +}; | |||
| + | |||
| +static void cfs_overlay_release(struct config_item *item) | |||
| +{ | |||
| + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); | |||
| + | |||
| + if (overlay->ov_id >= 0) | |||
| + of_overlay_remove(&overlay->ov_id); | |||
| + if (overlay->fw) | |||
| + release_firmware(overlay->fw); | |||
| + /* kfree with NULL is safe */ | |||
| + kfree(overlay->dtbo); | |||
| + kfree(overlay); | |||
| +} | |||
| + | |||
| +static struct configfs_item_operations cfs_overlay_item_ops = { | |||
| + .release = cfs_overlay_release, | |||
| +}; | |||
| + | |||
| +static struct config_item_type cfs_overlay_type = { | |||
| + .ct_item_ops = &cfs_overlay_item_ops, | |||
| + .ct_attrs = cfs_overlay_attrs, | |||
| + .ct_bin_attrs = cfs_overlay_bin_attrs, | |||
| + .ct_owner = THIS_MODULE, | |||
| +}; | |||
| + | |||
| +static struct config_item *cfs_overlay_group_make_item( | |||
| + struct config_group *group, const char *name) | |||
| +{ | |||
| + struct cfs_overlay_item *overlay; | |||
| + | |||
| + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); | |||
| + if (!overlay) | |||
| + return ERR_PTR(-ENOMEM); | |||
| + overlay->ov_id = -1; | |||
| + | |||
| + config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); | |||
| + return &overlay->item; | |||
| +} | |||
| + | |||
| +static void cfs_overlay_group_drop_item(struct config_group *group, | |||
| + struct config_item *item) | |||
| +{ | |||
| + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); | |||
| + | |||
| + config_item_put(&overlay->item); | |||
| +} | |||
| + | |||
| +static struct configfs_group_operations overlays_ops = { | |||
| + .make_item = cfs_overlay_group_make_item, | |||
| + .drop_item = cfs_overlay_group_drop_item, | |||
| +}; | |||
| + | |||
| +static struct config_item_type overlays_type = { | |||
| + .ct_group_ops = &overlays_ops, | |||
| + .ct_owner = THIS_MODULE, | |||
| +}; | |||
| + | |||
| +static struct configfs_group_operations of_cfs_ops = { | |||
| + /* empty - we don't allow anything to be created */ | |||
| +}; | |||
| + | |||
| +static struct config_item_type of_cfs_type = { | |||
| + .ct_group_ops = &of_cfs_ops, | |||
| + .ct_owner = THIS_MODULE, | |||
| +}; | |||
| + | |||
| +struct config_group of_cfs_overlay_group; | |||
| + | |||
| +static struct configfs_subsystem of_cfs_subsys = { | |||
| + .su_group = { | |||
| + .cg_item = { | |||
| + .ci_namebuf = "device-tree", | |||
| + .ci_type = &of_cfs_type, | |||
| + }, | |||
| + }, | |||
| + .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), | |||
| +}; | |||
| + | |||
| +static int __init of_cfs_init(void) | |||
| +{ | |||
| + int ret; | |||
| + | |||
| + pr_info("%s\n", __func__); | |||
| + | |||
| + config_group_init(&of_cfs_subsys.su_group); | |||
| + config_group_init_type_name(&of_cfs_overlay_group, "overlays", | |||
| + &overlays_type); | |||
| + configfs_add_default_group(&of_cfs_overlay_group, | |||
| + &of_cfs_subsys.su_group); | |||
| + | |||
| + ret = configfs_register_subsystem(&of_cfs_subsys); | |||
| + if (ret != 0) { | |||
| + pr_err("%s: failed to register subsys\n", __func__); | |||
| + goto out; | |||
| + } | |||
| + pr_info("%s: OK\n", __func__); | |||
| +out: | |||
| + return ret; | |||
| +} | |||
| +late_initcall(of_cfs_init); | |||
| @@ -0,0 +1,19 @@ | |||
| Some UART BT devices wrongly present themselves as SDIO - probably | |||
| because their WiFi counterparts are SDIO based. | |||
| This leads to creating a non functional fantom hci0 device. | |||
| This happens with AP6256 seen on both ROCK Pi 4 and OrangePi 4. | |||
| diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c | |||
| index 199e8f7d4..2edb53bfe 100644 | |||
| --- a/drivers/bluetooth/btsdio.c | |||
| +++ b/drivers/bluetooth/btsdio.c | |||
| @@ -295,6 +295,8 @@ static int btsdio_probe(struct sdio_func *func, | |||
| switch (func->device) { | |||
| case SDIO_DEVICE_ID_BROADCOM_43341: | |||
| case SDIO_DEVICE_ID_BROADCOM_43430: | |||
| + case SDIO_DEVICE_ID_BROADCOM_4345: | |||
| + case SDIO_DEVICE_ID_BROADCOM_43455: | |||
| case SDIO_DEVICE_ID_BROADCOM_4356: | |||
| return -ENODEV; | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| This patch is required to boot some Rock Pi 4 and NanoPC T4 units | |||
| with kernel 5.3+ and is on par with how it is done in Rockchip's BSP. | |||
| diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c | |||
| index de8fbc396..95858e554 100644 | |||
| --- a/drivers/mmc/core/mmc.c | |||
| +++ b/drivers/mmc/core/mmc.c | |||
| @@ -1371,12 +1371,12 @@ static int mmc_select_hs400es(struct mmc_card *card) | |||
| } | |||
| mmc_set_timing(host, MMC_TIMING_MMC_HS); | |||
| + mmc_set_clock(host, card->ext_csd.hs_max_dtr); | |||
| + | |||
| err = mmc_switch_status(card, true); | |||
| if (err) | |||
| goto out_err; | |||
| - mmc_set_clock(host, card->ext_csd.hs_max_dtr); | |||
| - | |||
| /* Switch card to DDR with strobe bit */ | |||
| val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE; | |||
| err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, | |||
| @@ -0,0 +1,11 @@ | |||
| --- a/sound/soc/codecs/es8316.c 2020-04-09 19:13:08.268473737 +0000 | |||
| +++ b/sound/soc/codecs/es8316.c 2020-04-09 19:14:00.535995842 +0000 | |||
| @@ -687,7 +687,7 @@ | |||
| snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, | |||
| ES8316_GPIO_ENABLE_INTERRUPT, 0); | |||
| - if (es8316->jack->status & SND_JACK_MICROPHONE) { | |||
| + if (es8316->jack && (es8316->jack->status & SND_JACK_MICROPHONE)) { | |||
| es8316_disable_micbias_for_mic_gnd_short_detect(component); | |||
| snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0); | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c | |||
| index 6bc74a2d5..e3827da51 100644 | |||
| --- a/kernel/dma/pool.c | |||
| +++ b/kernel/dma/pool.c | |||
| @@ -164,13 +164,11 @@ static int __init dma_atomic_pool_init(void) | |||
| int ret = 0; | |||
| /* | |||
| - * If coherent_pool was not used on the command line, default the pool | |||
| - * sizes to 128KB per 1GB of memory, min 128KB, max MAX_ORDER-1. | |||
| + * Always use 2MiB as default pool size. | |||
| + * See: https://forum.armbian.com/topic/4811-uas-mainline-kernel-coherent-pool-memory-size/ | |||
| */ | |||
| if (!atomic_pool_size) { | |||
| - unsigned long pages = totalram_pages() / (SZ_1G / SZ_128K); | |||
| - pages = min_t(unsigned long, pages, MAX_ORDER_NR_PAGES); | |||
| - atomic_pool_size = max_t(size_t, pages << PAGE_SHIFT, SZ_128K); | |||
| + atomic_pool_size = SZ_2M; | |||
| } | |||
| INIT_WORK(&atomic_pool_work, atomic_pool_work_fn); | |||
| @@ -0,0 +1,490 @@ | |||
| diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile | |||
| index 6a33eff..60fc91a 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/Makefile | |||
| +++ b/arch/arm64/boot/dts/rockchip/Makefile | |||
| @@ -17,3 +17,5 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-puma-haikou.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire-excavator.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399pro-rock-pi-n10.dtb | |||
| + | |||
| +subdir-y := $(dts-dirs) overlay | |||
| diff --git a/arch/arm64/boot/dts/rockchip/overlay/Makefile b/arch/arm64/boot/dts/rockchip/overlay/Makefile | |||
| index e69de29..576e190 100644 | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/overlay/Makefile | |||
| @@ -0,0 +1,22 @@ | |||
| +# SPDX-License-Identifier: GPL-2.0 | |||
| +dtbo-$(CONFIG_ARCH_ROCKCHIP) += \ | |||
| + rockchip-i2c7.dtbo \ | |||
| + rockchip-i2c8.dtbo \ | |||
| + rockchip-pcie-gen2.dtbo \ | |||
| + rockchip-spi-jedec-nor.dtbo \ | |||
| + rockchip-spi-spidev.dtbo \ | |||
| + rockchip-uart4.dtbo \ | |||
| + rockchip-w1-gpio.dtbo | |||
| + | |||
| +scr-$(CONFIG_ARCH_ROCKCHIP) += \ | |||
| + rockchip-fixup.scr | |||
| + | |||
| +dtbotxt-$(CONFIG_ARCH_ROCKCHIP) += \ | |||
| + README.rockchip-overlays | |||
| + | |||
| +targets += $(dtbo-y) $(scr-y) $(dtbotxt-y) | |||
| + | |||
| +always := $(dtbo-y) $(scr-y) $(dtbotxt-y) | |||
| +clean-files := *.dtbo *.scr | |||
| + | |||
| + | |||
| diff --git a/arch/arm64/boot/dts/rockchip/overlay/README.rockchip-overlays b/arch/arm64/boot/dts/rockchip/overlay/README.rockchip-overlays | |||
| index e69de29..9512445 100644 | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/overlay/README.rockchip-overlays | |||
| @@ -0,0 +1,106 @@ | |||
| +This document describes overlays provided in the kernel packages | |||
| +For generic Armbian overlays documentation please see | |||
| +https://docs.armbian.com/User-Guide_Allwinner_overlays/ | |||
| + | |||
| +### Platform: | |||
| + | |||
| +rockchip (Rockchip) | |||
| + | |||
| +### Provided overlays: | |||
| + | |||
| +- i2c7, i2c8, pcie-gen2, spi-spidev, uart4, w1-gpio | |||
| + | |||
| +### Overlay details: | |||
| + | |||
| +### i2c7 | |||
| + | |||
| +Activates TWI/I2C bus 7 | |||
| + | |||
| +I2C7 pins (SCL, SDA): GPIO2-B0, GPIO2-A7 GPIO1-C5, GPIO1-C4 | |||
| + | |||
| +### i2c8 | |||
| + | |||
| +Activates TWI/I2C bus 8 | |||
| + | |||
| +I2C8 pins (SCL, SDA): GPIO1-C5, GPIO1-C4 | |||
| + | |||
| +### pcie-gen2 | |||
| + | |||
| +Enables PCIe Gen2 link speed on RK3399. | |||
| +WARNING! Not officially supported by Rockchip!!! | |||
| + | |||
| +### spi-jedec-nor | |||
| + | |||
| +Activates MTD support for JEDEC compatible SPI NOR flash chips on SPI bus | |||
| +supported by the kernel SPI NOR driver | |||
| + | |||
| +SPI 0 pins (MOSI, MISO, SCK, CS): GPIO3_A5, GPIO3_A4, GPIO3_A6, GPIO3_A7 | |||
| +SPI 1 pins (MOSI, MISO, SCK, CS): GPIO1_A7, GPIO1_B0, GPIO1_B1, GPIO1_B2 | |||
| +SPI 2 pins (MOSI, MISO, SCK, CS): GPIO1_C0, GPIO1_B7, GPIO1_C1, GPIO1_C2 | |||
| +SPI 3 pins (MOSI, MISO, SCK, CS): GPIO2_B2, GPIO2_B1, GPIO2_B3, GPIO2_B4 | |||
| + | |||
| +Parameters: | |||
| + | |||
| +param_spinor_spi_bus (int) | |||
| + SPI bus to activate SPI NOR flash support on | |||
| + Required | |||
| + Supported values: 0, 1, 2 | |||
| + | |||
| +param_spinor_max_freq (int) | |||
| + Maximum SPI frequency | |||
| + Optional | |||
| + Default: 1000000 | |||
| + Range: 3000 - 100000000 | |||
| + | |||
| +### spi-spidev | |||
| + | |||
| +Activates SPIdev device node (/dev/spidevX.Y) for userspace SPI access, | |||
| +where X is the bus number and Y is the CS number | |||
| + | |||
| +SPI 0 pins (MOSI, MISO, SCK, CS): GPIO3_A5, GPIO3_A4, GPIO3_A6, GPIO3_A7 | |||
| +SPI 1 pins (MOSI, MISO, SCK, CS): GPIO1_A7, GPIO1_B0, GPIO1_B1, GPIO1_B2 | |||
| +SPI 2 pins (MOSI, MISO, SCK, CS): GPIO1_C0, GPIO1_B7, GPIO1_C1, GPIO1_C2 | |||
| +SPI 3 pins (MOSI, MISO, SCK, CS): GPIO2_B2, GPIO2_B1, GPIO2_B3, GPIO2_B4 | |||
| + | |||
| +Parameters: | |||
| + | |||
| +param_spidev_spi_bus (int) | |||
| + SPI bus to activate SPIdev support on | |||
| + Required | |||
| + Supported values: 0, 1 | |||
| + | |||
| +param_spidev_spi_cs (int) | |||
| + SPI chip select number | |||
| + Optional | |||
| + Default: 0 | |||
| + Supported values: 0, 1 | |||
| + Using chip select 1 requires using "spi-add-cs1" overlay | |||
| + | |||
| +param_spidev_max_freq (int) | |||
| + Maximum SPIdev frequency | |||
| + Optional | |||
| + Default: 1000000 | |||
| + Range: 3000 - 100000000 | |||
| + | |||
| +### uart4 | |||
| + | |||
| +Activates UART4 | |||
| + | |||
| +UART4 pins (RX, TX): GPIO1_A7, GPIO1_B0 | |||
| + | |||
| +Notice: UART4 cannot be activated together with SPI1 - they share the sam pins. | |||
| +Enabling this overlay disables SPI1. | |||
| + | |||
| +### w1-gpio | |||
| + | |||
| +Activates 1-Wire GPIO master | |||
| +Requires an external pull-up resistor on the data pin | |||
| +or enabling the internal pull-up | |||
| + | |||
| +Parameters: | |||
| + | |||
| +param_w1_pin (pin) | |||
| + Data pin for 1-Wire master | |||
| + Optional | |||
| + Default: PD14 | |||
| + | |||
| diff --git a/arch/arm64/boot/dts/rockchip/overlay/rockchip-fixup.scr-cmd b/arch/arm64/boot/dts/rockchip/overlay/rockchip-fixup.scr-cmd | |||
| new file mode 100644 | |||
| index 0000000..d4c39e2 | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/overlay/rockchip-fixup.scr-cmd | |||
| @@ -0,0 +1,62 @@ | |||
| +# overlays fixup script | |||
| +# implements (or rather substitutes) overlay arguments functionality | |||
| +# using u-boot scripting, environment variables and "fdt" command | |||
| + | |||
| +setenv decompose_pin 'setexpr tmp_pinctrl sub "GPIO(0|1|2|3|4)_\\S\\d+" "\\1"; | |||
| +setexpr tmp_bank sub "GPIO\\d_(\\S)\\d+" "\\1"; | |||
| +test "${tmp_bank}" = "A" && setenv tmp_bank 0; | |||
| +test "${tmp_bank}" = "B" && setenv tmp_bank 1; | |||
| +test "${tmp_bank}" = "C" && setenv tmp_bank 2; | |||
| +test "${tmp_bank}" = "D" && setenv tmp_bank 3; | |||
| +setexpr tmp_pin sub "GPIO\\d_\\S(\\d+)" "\\1"; | |||
| +setexpr tmp_bank ${tmp_bank} * 8; | |||
| +setexpr tmp_pin ${tmp_bank} + ${tmp_pin}' | |||
| + | |||
| + | |||
| +if test -n "${param_spinor_spi_bus}"; then | |||
| + test "${param_spinor_spi_bus}" = "0" && setenv tmp_spi_path "spi@ff1c0000" | |||
| + test "${param_spinor_spi_bus}" = "1" && setenv tmp_spi_path "spi@ff1d0000" | |||
| + test "${param_spinor_spi_bus}" = "2" && setenv tmp_spi_path "spi@ff1e0000" | |||
| + test "${param_spinor_spi_bus}" = "3" && setenv tmp_spi_path "spi@ff1f0000" | |||
| + fdt set /${tmp_spi_path} status "okay" | |||
| + fdt set /${tmp_spi_path}/spiflash@0 status "okay" | |||
| + if test -n "${param_spinor_max_freq}"; then | |||
| + fdt set /${tmp_spi_path}/spiflash@0 spi-max-frequency "<${param_spinor_max_freq}>" | |||
| + fi | |||
| + if test "${param_spinor_spi_cs}" = "1"; then | |||
| + fdt set /${tmp_spi_path}/spiflash@0 reg "<1>" | |||
| + fi | |||
| + env delete tmp_spi_path | |||
| +fi | |||
| + | |||
| +if test -n "${param_spidev_spi_bus}"; then | |||
| + test "${param_spidev_spi_bus}" = "0" && setenv tmp_spi_path "spi@ff1c0000" | |||
| + test "${param_spidev_spi_bus}" = "1" && setenv tmp_spi_path "spi@ff1d0000" | |||
| + test "${param_spidev_spi_bus}" = "2" && setenv tmp_spi_path "spi@ff1e0000" | |||
| + test "${param_spidev_spi_bus}" = "3" && setenv tmp_spi_path "spi@ff1f0000" | |||
| + fdt set /${tmp_spi_path} status "okay" | |||
| + fdt set /${tmp_spi_path}/spidev status "okay" | |||
| + if test -n "${param_spidev_max_freq}"; then | |||
| + fdt set /${tmp_spi_path}/spidev spi-max-frequency "<${param_spidev_max_freq}>" | |||
| + fi | |||
| + if test "${param_spidev_spi_cs}" = "1"; then | |||
| + fdt set /${tmp_spi_path}/spidev reg "<1>"; | |||
| + fi | |||
| +fi | |||
| + | |||
| +if test -n "${param_w1_pin}"; then | |||
| + setenv tmp_pinctrl "${param_w1_pin}" | |||
| + setenv tmp_bank "${param_w1_pin}" | |||
| + setenv tmp_pin "${param_w1_pin}" | |||
| + run decompose_pin | |||
| + #echo "${param_w1_pin} ---> pinctrl = ${tmp_pinctrl}" | |||
| + #echo "${param_w1_pin} ---> bank = ${tmp_bank}" | |||
| + #echo "${param_w1_pin} ---> pin = ${tmp_pin}" | |||
| + fdt get value tmp_pinctrl /__symbols__ gpio${tmp_pinctrl} | |||
| + #echo "${param_w1_pin} ---> tmp_pinctrl = ${tmp_pinctrl}" | |||
| + fdt get value tmp_phandle ${tmp_pinctrl} phandle | |||
| + #echo "${param_w1_pin} ---> tmp_phandle = ${tmp_phandle}" | |||
| + fdt set /onewire@0 gpios "<${tmp_phandle} 0x000000${tmp_pin} 0 0>" | |||
| + env delete tmp_pinctrl tmp_bank tmp_pin tmp_phandle | |||
| +fi | |||
| + | |||
| diff --git a/arch/arm64/boot/dts/rockchip/overlay/rockchip-i2c7.dts b/arch/arm64/boot/dts/rockchip/overlay/rockchip-i2c7.dts | |||
| new file mode 100644 | |||
| index 0000000..54bc844 | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/overlay/rockchip-i2c7.dts | |||
| @@ -0,0 +1,11 @@ | |||
| +/dts-v1/; | |||
| + | |||
| +/ { | |||
| + compatible = "rockchip,rk3399"; | |||
| + fragment@0 { | |||
| + target-path = "/i2c@ff160000"; | |||
| + __overlay__ { | |||
| + status = "okay"; | |||
| + }; | |||
| + }; | |||
| +}; | |||
| diff --git a/arch/arm64/boot/dts/rockchip/overlay/rockchip-i2c8.dts b/arch/arm64/boot/dts/rockchip/overlay/rockchip-i2c8.dts | |||
| new file mode 100644 | |||
| index 0000000..54bc844 | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/overlay/rockchip-i2c8.dts | |||
| @@ -0,0 +1,11 @@ | |||
| +/dts-v1/; | |||
| + | |||
| +/ { | |||
| + compatible = "rockchip,rk3399"; | |||
| + fragment@0 { | |||
| + target-path = "/i2c@ff3e0000"; | |||
| + __overlay__ { | |||
| + status = "okay"; | |||
| + }; | |||
| + }; | |||
| +}; | |||
| diff --git a/arch/arm64/boot/dts/rockchip/overlay/rockchip-pcie-gen2.dts b/arch/arm64/boot/dts/rockchip/overlay/rockchip-pcie-gen2.dts | |||
| new file mode 100644 | |||
| index 0000000..54bc844 | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/overlay/rockchip-pcie-gen2.dts | |||
| @@ -0,0 +1,12 @@ | |||
| +/dts-v1/; | |||
| +/plugin/; | |||
| + | |||
| +/ { | |||
| + compatible = "rockchip,rk3399"; | |||
| + fragment@0 { | |||
| + target = <&pcie0>; | |||
| + __overlay__ { | |||
| + max-link-speed = <2>; | |||
| + }; | |||
| + }; | |||
| +}; | |||
| diff --git a/arch/arm64/boot/dts/rockchip/overlay/rockchip-spi-jedec-nor.dts b/arch/arm64/boot/dts/rockchip/overlay/rockchip-spi-jedec-nor.dts | |||
| new file mode 100644 | |||
| index 0000000..3a2be38 | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/overlay/rockchip-spi-jedec-nor.dts | |||
| @@ -0,0 +1,72 @@ | |||
| +/dts-v1/; | |||
| +/plugin/; | |||
| + | |||
| +/ { | |||
| + compatible = "rockchip,rk3399"; | |||
| + | |||
| + fragment@0 { | |||
| + target-path = "/aliases"; | |||
| + __overlay__ { | |||
| + spi0 = "/spi@ff1c0000"; | |||
| + spi1 = "/spi@ff1d0000"; | |||
| + spi2 = "/spi@ff1e0000"; | |||
| + spi3 = "/spi@ff1f0000"; | |||
| + }; | |||
| + }; | |||
| + | |||
| + fragment@1 { | |||
| + target = <&spi0>; | |||
| + __overlay__ { | |||
| + #address-cells = <1>; | |||
| + #size-cells = <0>; | |||
| + spiflash@0 { | |||
| + compatible = "jedec,spi-nor"; | |||
| + reg = <0>; | |||
| + spi-max-frequency = <10000000>; | |||
| + status = "disabled"; | |||
| + }; | |||
| + }; | |||
| + }; | |||
| + | |||
| + fragment@2 { | |||
| + target = <&spi1>; | |||
| + __overlay__ { | |||
| + #address-cells = <1>; | |||
| + #size-cells = <0>; | |||
| + spiflash@0 { | |||
| + compatible = "jedec,spi-nor"; | |||
| + reg = <0>; | |||
| + spi-max-frequency = <10000000>; | |||
| + status = "disabled"; | |||
| + }; | |||
| + }; | |||
| + }; | |||
| + | |||
| + fragment@3 { | |||
| + target = <&spi2>; | |||
| + __overlay__ { | |||
| + #address-cells = <1>; | |||
| + #size-cells = <0>; | |||
| + spiflash@0 { | |||
| + compatible = "jedec,spi-nor"; | |||
| + reg = <0>; | |||
| + spi-max-frequency = <10000000>; | |||
| + status = "disabled"; | |||
| + }; | |||
| + }; | |||
| + }; | |||
| + | |||
| + fragment@4 { | |||
| + target = <&spi3>; | |||
| + __overlay__ { | |||
| + #address-cells = <1>; | |||
| + #size-cells = <0>; | |||
| + spiflash@0 { | |||
| + compatible = "jedec,spi-nor"; | |||
| + reg = <0>; | |||
| + spi-max-frequency = <10000000>; | |||
| + status = "disabled"; | |||
| + }; | |||
| + }; | |||
| + }; | |||
| +}; | |||
| diff --git a/arch/arm64/boot/dts/rockchip/overlay/rockchip-spi-spidev.dts b/arch/arm64/boot/dts/rockchip/overlay/rockchip-spi-spidev.dts | |||
| new file mode 100644 | |||
| index 0000000..fe8fb14 | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/overlay/rockchip-spi-spidev.dts | |||
| @@ -0,0 +1,72 @@ | |||
| +/dts-v1/; | |||
| +/plugin/; | |||
| + | |||
| +/ { | |||
| + compatible = "rockchip,rk3399"; | |||
| + | |||
| + fragment@0 { | |||
| + target-path = "/aliases"; | |||
| + __overlay__ { | |||
| + spi0 = "/spi@ff1c0000"; | |||
| + spi1 = "/spi@ff1d0000"; | |||
| + spi2 = "/spi@ff1e0000"; | |||
| + spi3 = "/spi@ff1f0000"; | |||
| + }; | |||
| + }; | |||
| + | |||
| + fragment@1 { | |||
| + target = <&spi0>; | |||
| + __overlay__ { | |||
| + #address-cells = <1>; | |||
| + #size-cells = <0>; | |||
| + spidev { | |||
| + compatible = "spidev"; | |||
| + status = "disabled"; | |||
| + reg = <0>; | |||
| + spi-max-frequency = <10000000>; | |||
| + }; | |||
| + }; | |||
| + }; | |||
| + | |||
| + fragment@2 { | |||
| + target = <&spi1>; | |||
| + __overlay__ { | |||
| + #address-cells = <1>; | |||
| + #size-cells = <0>; | |||
| + spidev { | |||
| + compatible = "spidev"; | |||
| + status = "disabled"; | |||
| + reg = <0>; | |||
| + spi-max-frequency = <10000000>; | |||
| + }; | |||
| + }; | |||
| + }; | |||
| + | |||
| + fragment@3 { | |||
| + target = <&spi2>; | |||
| + __overlay__ { | |||
| + #address-cells = <1>; | |||
| + #size-cells = <0>; | |||
| + spidev { | |||
| + compatible = "spidev"; | |||
| + status = "disabled"; | |||
| + reg = <0>; | |||
| + spi-max-frequency = <10000000>; | |||
| + }; | |||
| + }; | |||
| + }; | |||
| + | |||
| + fragment@4 { | |||
| + target = <&spi3>; | |||
| + __overlay__ { | |||
| + #address-cells = <1>; | |||
| + #size-cells = <0>; | |||
| + spidev { | |||
| + compatible = "spidev"; | |||
| + status = "disabled"; | |||
| + reg = <0>; | |||
| + spi-max-frequency = <10000000>; | |||
| + }; | |||
| + }; | |||
| + }; | |||
| +}; | |||
| diff --git a/arch/arm64/boot/dts/rockchip/overlay/rockchip-uart4.dts b/arch/arm64/boot/dts/rockchip/overlay/rockchip-uart4.dts | |||
| new file mode 100644 | |||
| index 0000000..fe8fb14 | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/overlay/rockchip-uart4.dts | |||
| @@ -0,0 +1,20 @@ | |||
| +/dts-v1/; | |||
| +/plugin/; | |||
| + | |||
| +/ { | |||
| + compatible = "rockchip,rk3399"; | |||
| + | |||
| + fragment@0 { | |||
| + target = <&spi1>; | |||
| + __overlay__ { | |||
| + status = "disabled"; | |||
| + }; | |||
| + }; | |||
| + | |||
| + fragment@1 { | |||
| + target = <&uart4>; | |||
| + __overlay__ { | |||
| + status = "okay"; | |||
| + }; | |||
| + }; | |||
| +}; | |||
| diff --git a/arch/arm64/boot/dts/rockchip/overlay/rockchip-w1-gpio.dts b/arch/arm64/boot/dts/rockchip/overlay/rockchip-w1-gpio.dts | |||
| new file mode 100644 | |||
| index 0000000..bfbc16a | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/overlay/rockchip-w1-gpio.dts | |||
| @@ -0,0 +1,20 @@ | |||
| +// Definitions for w1-gpio module (without external pullup) | |||
| +/dts-v1/; | |||
| +/plugin/; | |||
| + | |||
| +/ { | |||
| + compatible = "rockchip,rk3399"; | |||
| + | |||
| + fragment@0 { | |||
| + target-path = "/"; | |||
| + __overlay__ { | |||
| + | |||
| + w1: onewire@0 { | |||
| + compatible = "w1-gpio"; | |||
| + pinctrl-names = "default"; | |||
| + gpios = <&gpio1 4 0 0xae>; // GPIO1_A4 | |||
| + status = "okay"; | |||
| + }; | |||
| + }; | |||
| + }; | |||
| +}; | |||
| diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib | |||
| index 26e6af4..65b9435 100644 | |||
| --- a/scripts/Makefile.lib | |||
| +++ b/scripts/Makefile.lib | |||
| @@ -65,6 +65,9 @@ real-objs-m := $(foreach m, $(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y) | |||
| extra-y += $(dtb-y) | |||
| extra-$(CONFIG_OF_ALL_DTBS) += $(dtb-) | |||
| +# Overlay targets | |||
| +extra-y += $(dtbo-y) $(scr-y) $(dtbotxt-y) | |||
| + | |||
| # Add subdir path | |||
| extra-y := $(addprefix $(obj)/,$(extra-y)) | |||
| @@ -0,0 +1,60 @@ | |||
| diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c | |||
| index c506c9305..41a08b320 100644 | |||
| --- a/sound/soc/codecs/rt5651.c | |||
| +++ b/sound/soc/codecs/rt5651.c | |||
| @@ -24,6 +24,7 @@ | |||
| #include <sound/initval.h> | |||
| #include <sound/tlv.h> | |||
| #include <sound/jack.h> | |||
| +#include <linux/clk.h> | |||
| #include "rl6231.h" | |||
| #include "rt5651.h" | |||
| @@ -1511,6 +1512,7 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, | |||
| static int rt5651_set_bias_level(struct snd_soc_component *component, | |||
| enum snd_soc_bias_level level) | |||
| { | |||
| + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); | |||
| switch (level) { | |||
| case SND_SOC_BIAS_PREPARE: | |||
| if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) { | |||
| @@ -1518,6 +1520,13 @@ static int rt5651_set_bias_level(struct snd_soc_component *component, | |||
| snd_soc_component_update_bits(component, RT5651_D_MISC, | |||
| 0xc00, 0xc00); | |||
| } | |||
| + if (!IS_ERR(rt5651->mclk)){ | |||
| + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { | |||
| + clk_disable_unprepare(rt5651->mclk); | |||
| + } else { | |||
| + clk_prepare_enable(rt5651->mclk); | |||
| + } | |||
| + } | |||
| break; | |||
| case SND_SOC_BIAS_STANDBY: | |||
| if (SND_SOC_BIAS_OFF == snd_soc_component_get_bias_level(component)) { | |||
| @@ -2059,6 +2068,13 @@ static int rt5651_probe(struct snd_soc_component *component) | |||
| { | |||
| struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); | |||
| + /* Check if MCLK provided */ | |||
| + rt5651->mclk = devm_clk_get(component->dev, "mclk"); | |||
| + if (PTR_ERR(rt5651->mclk) == -EPROBE_DEFER){ | |||
| + dev_err(component->dev, "unable to get mclk\n"); | |||
| + return -EPROBE_DEFER; | |||
| + } | |||
| + | |||
| rt5651->component = component; | |||
| snd_soc_component_update_bits(component, RT5651_PWR_ANLG1, | |||
| diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h | |||
| index 20c33a3ec..17524fa9f 100644 | |||
| --- a/sound/soc/codecs/rt5651.h | |||
| +++ b/sound/soc/codecs/rt5651.h | |||
| @@ -2097,6 +2097,7 @@ struct rt5651_priv { | |||
| int dmic_en; | |||
| bool hp_mute; | |||
| + struct clk *mclk; | |||
| }; | |||
| #endif /* __RT5651_H__ */ | |||
| @@ -0,0 +1,25 @@ | |||
| diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c | |||
| index ce1d2446f..38447441b 100644 | |||
| --- a/drivers/clk/rockchip/clk-rk3399.c | |||
| +++ b/drivers/clk/rockchip/clk-rk3399.c | |||
| @@ -620,7 +620,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { | |||
| GATE(SCLK_I2S2_8CH, "clk_i2s2", "clk_i2s2_mux", CLK_SET_RATE_PARENT, | |||
| RK3399_CLKGATE_CON(8), 11, GFLAGS), | |||
| - MUX(0, "clk_i2sout_src", mux_i2sch_p, CLK_SET_RATE_PARENT, | |||
| + MUX(SCLK_I2SOUT_SRC, "clk_i2sout_src", mux_i2sch_p, CLK_SET_RATE_PARENT, | |||
| RK3399_CLKSEL_CON(31), 0, 2, MFLAGS), | |||
| COMPOSITE_NODIV(SCLK_I2S_8CH_OUT, "clk_i2sout", mux_i2sout_p, CLK_SET_RATE_PARENT, | |||
| RK3399_CLKSEL_CON(31), 2, 1, MFLAGS, | |||
| diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h | |||
| index 44e0a319f..b7b07dfda 100644 | |||
| --- a/include/dt-bindings/clock/rk3399-cru.h | |||
| +++ b/include/dt-bindings/clock/rk3399-cru.h | |||
| @@ -19,6 +19,7 @@ | |||
| #define ARMCLKB 9 | |||
| /* sclk gates (special clocks) */ | |||
| +#define SCLK_I2SOUT_SRC 64 | |||
| #define SCLK_I2C1 65 | |||
| #define SCLK_I2C2 66 | |||
| #define SCLK_I2C3 67 | |||
| @@ -0,0 +1,40 @@ | |||
| The change is in line with the following commit from Rockchip's BSP: | |||
| https://github.com/rockchip-linux/kernel/commit/7a8a38540302b746cdb1238023d807190c1ee485 | |||
| "It is better to make the voltage greater than 810mV | |||
| and it will be more stable." | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi | |||
| index 30f353c10..996fe66a5 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi | |||
| @@ -10,12 +10,12 @@ | |||
| opp00 { | |||
| opp-hz = /bits/ 64 <408000000>; | |||
| - opp-microvolt = <800000>; | |||
| + opp-microvolt = <825000>; | |||
| clock-latency-ns = <40000>; | |||
| }; | |||
| opp01 { | |||
| opp-hz = /bits/ 64 <600000000>; | |||
| - opp-microvolt = <800000>; | |||
| + opp-microvolt = <825000>; | |||
| }; | |||
| opp02 { | |||
| opp-hz = /bits/ 64 <816000000>; | |||
| @@ -45,12 +45,12 @@ | |||
| opp00 { | |||
| opp-hz = /bits/ 64 <408000000>; | |||
| - opp-microvolt = <800000>; | |||
| + opp-microvolt = <825000>; | |||
| clock-latency-ns = <40000>; | |||
| }; | |||
| opp01 { | |||
| opp-hz = /bits/ 64 <600000000>; | |||
| - opp-microvolt = <800000>; | |||
| + opp-microvolt = <825000>; | |||
| }; | |||
| opp02 { | |||
| opp-hz = /bits/ 64 <816000000>; | |||
| @@ -0,0 +1,49 @@ | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| index 6eb9dda..d6fc676 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| @@ -2285,35 +2285,35 @@ | |||
| sdmmc { | |||
| sdmmc_bus1: sdmmc-bus1 { | |||
| rockchip,pins = | |||
| - <4 RK_PB0 1 &pcfg_pull_up>; | |||
| + <4 RK_PB0 1 &pcfg_pull_up_8ma>; | |||
| }; | |||
| sdmmc_bus4: sdmmc-bus4 { | |||
| rockchip,pins = | |||
| - <4 RK_PB0 1 &pcfg_pull_up>, | |||
| - <4 RK_PB1 1 &pcfg_pull_up>, | |||
| - <4 RK_PB2 1 &pcfg_pull_up>, | |||
| - <4 RK_PB3 1 &pcfg_pull_up>; | |||
| + <4 RK_PB0 1 &pcfg_pull_up_8ma>, | |||
| + <4 RK_PB1 1 &pcfg_pull_up_8ma>, | |||
| + <4 RK_PB2 1 &pcfg_pull_up_8ma>, | |||
| + <4 RK_PB3 1 &pcfg_pull_up_8ma>; | |||
| }; | |||
| sdmmc_clk: sdmmc-clk { | |||
| rockchip,pins = | |||
| - <4 RK_PB4 1 &pcfg_pull_none>; | |||
| + <4 RK_PB4 1 &pcfg_pull_none_12ma>; | |||
| }; | |||
| sdmmc_cmd: sdmmc-cmd { | |||
| rockchip,pins = | |||
| - <4 RK_PB5 1 &pcfg_pull_up>; | |||
| + <4 RK_PB5 1 &pcfg_pull_up_8ma>; | |||
| }; | |||
| sdmmc_cd: sdmmc-cd { | |||
| rockchip,pins = | |||
| - <0 RK_PA7 1 &pcfg_pull_up>; | |||
| + <0 RK_PA7 1 &pcfg_pull_up>; | |||
| }; | |||
| sdmmc_wp: sdmmc-wp { | |||
| rockchip,pins = | |||
| - <0 RK_PB0 1 &pcfg_pull_up>; | |||
| + <0 RK_PB0 1 &pcfg_pull_up_8ma>; | |||
| }; | |||
| }; | |||
| @@ -0,0 +1,1602 @@ | |||
| From bbfdef43b6f4a65b960ebe6e540233a4c26e9374 Mon Sep 17 00:00:00 2001 | |||
| From: Ezequiel Garcia <ezequiel@collabora.com> | |||
| Date: Fri, 26 Jun 2020 14:11:29 -0300 | |||
| Subject: [PATCH 01/18] rkvdec: h264: Refuse to decode unsupported bitstream | |||
| The hardware only supports 4:2:2, 4:2:0 or 4:0:0 (monochrome), | |||
| 8-bit or 10-bit depth content. | |||
| Verify that the PPS refers to a supported bitstream, and refuse | |||
| unsupported bitstreams by failing at TRY_EXT_CTRLS time. | |||
| The driver is currently broken on 10-bit and 4:2:2 | |||
| so disallow those as well. | |||
| Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec.c | 27 +++++++++++++++++++++++++++ | |||
| 1 file changed, 27 insertions(+) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c | |||
| index 225eeca73356..0f81b47792f6 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec.c | |||
| @@ -27,6 +27,32 @@ | |||
| #include "rkvdec.h" | |||
| #include "rkvdec-regs.h" | |||
| +static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) | |||
| +{ | |||
| + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { | |||
| + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_cur.p; | |||
| + /* | |||
| + * TODO: The hardware supports 10-bit and 4:2:2 profiles, | |||
| + * but it's currently broken in the driver. | |||
| + * Reject them for now, until it's fixed. | |||
| + */ | |||
| + if (sps->chroma_format_idc > 1) | |||
| + /* Only 4:0:0 and 4:2:0 are supported */ | |||
| + return -EINVAL; | |||
| + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) | |||
| + /* Luma and chroma bit depth mismatch */ | |||
| + return -EINVAL; | |||
| + if (sps->bit_depth_luma_minus8 != 0) | |||
| + /* Only 8-bit is supported */ | |||
| + return -EINVAL; | |||
| + } | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { | |||
| + .try_ctrl = rkvdec_try_ctrl, | |||
| +}; | |||
| + | |||
| static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { | |||
| { | |||
| .per_request = true, | |||
| @@ -42,6 +68,7 @@ static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { | |||
| .per_request = true, | |||
| .mandatory = true, | |||
| .cfg.id = V4L2_CID_MPEG_VIDEO_H264_SPS, | |||
| + .cfg.ops = &rkvdec_ctrl_ops, | |||
| }, | |||
| { | |||
| .per_request = true, | |||
| -- | |||
| 2.25.1 | |||
| From 843e3576055776df5cafb3f8e28ea08f9fa71e9a Mon Sep 17 00:00:00 2001 | |||
| From: Ezequiel Garcia <ezequiel@collabora.com> | |||
| Date: Fri, 26 Jun 2020 14:11:30 -0300 | |||
| Subject: [PATCH 02/18] hantro: h264: Refuse to decode unsupported bitstream | |||
| The hardware only supports 4:2:0 or 4:0:0 (monochrome), | |||
| 8-bit depth content. | |||
| Verify that the PPS refers to a supported bitstream, and refuse | |||
| unsupported bitstreams by failing at TRY_EXT_CTRLS time. | |||
| Given the JPEG compression level control is the only one | |||
| that needs setting, a specific ops is provided. | |||
| Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com> | |||
| --- | |||
| drivers/staging/media/hantro/hantro_drv.c | 29 ++++++++++++++++++++--- | |||
| 1 file changed, 26 insertions(+), 3 deletions(-) | |||
| diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c | |||
| index 0db8ad455160..361ffaa821ef 100644 | |||
| --- a/drivers/staging/media/hantro/hantro_drv.c | |||
| +++ b/drivers/staging/media/hantro/hantro_drv.c | |||
| @@ -261,7 +261,25 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) | |||
| return vb2_queue_init(dst_vq); | |||
| } | |||
| -static int hantro_s_ctrl(struct v4l2_ctrl *ctrl) | |||
| +static int hantro_try_ctrl(struct v4l2_ctrl *ctrl) | |||
| +{ | |||
| + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { | |||
| + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_cur.p; | |||
| + | |||
| + if (sps->chroma_format_idc > 1) | |||
| + /* Only 4:0:0 and 4:2:0 are supported */ | |||
| + return -EINVAL; | |||
| + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) | |||
| + /* Luma and chroma bit depth mismatch */ | |||
| + return -EINVAL; | |||
| + if (sps->bit_depth_luma_minus8 != 0) | |||
| + /* Only 8-bit is supported */ | |||
| + return -EINVAL; | |||
| + } | |||
| + return 0; | |||
| +} | |||
| + | |||
| +static int hantro_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) | |||
| { | |||
| struct hantro_ctx *ctx; | |||
| @@ -282,7 +300,11 @@ static int hantro_s_ctrl(struct v4l2_ctrl *ctrl) | |||
| } | |||
| static const struct v4l2_ctrl_ops hantro_ctrl_ops = { | |||
| - .s_ctrl = hantro_s_ctrl, | |||
| + .try_ctrl = hantro_try_ctrl, | |||
| +}; | |||
| + | |||
| +static const struct v4l2_ctrl_ops hantro_jpeg_ctrl_ops = { | |||
| + .s_ctrl = hantro_jpeg_s_ctrl, | |||
| }; | |||
| static const struct hantro_ctrl controls[] = { | |||
| @@ -294,7 +316,7 @@ static const struct hantro_ctrl controls[] = { | |||
| .max = 100, | |||
| .step = 1, | |||
| .def = 50, | |||
| - .ops = &hantro_ctrl_ops, | |||
| + .ops = &hantro_jpeg_ctrl_ops, | |||
| }, | |||
| }, { | |||
| .codec = HANTRO_MPEG2_DECODER, | |||
| @@ -325,6 +347,7 @@ static const struct hantro_ctrl controls[] = { | |||
| .codec = HANTRO_H264_DECODER, | |||
| .cfg = { | |||
| .id = V4L2_CID_MPEG_VIDEO_H264_SPS, | |||
| + .ops = &hantro_ctrl_ops, | |||
| }, | |||
| }, { | |||
| .codec = HANTRO_H264_DECODER, | |||
| -- | |||
| 2.25.1 | |||
| From 22400b16c3d7af8cf3b044277264cc5fc3a07c54 Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Sun, 5 Jul 2020 21:48:56 +0000 | |||
| Subject: [PATCH 03/18] fixup! rkvdec: h264: Refuse to decode unsupported | |||
| bitstream | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec.c | 2 +- | |||
| 1 file changed, 1 insertion(+), 1 deletion(-) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c | |||
| index 0f81b47792f6..55dc27171ce4 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec.c | |||
| @@ -30,7 +30,7 @@ | |||
| static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) | |||
| { | |||
| if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { | |||
| - const struct v4l2_ctrl_h264_sps *sps = ctrl->p_cur.p; | |||
| + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p; | |||
| /* | |||
| * TODO: The hardware supports 10-bit and 4:2:2 profiles, | |||
| * but it's currently broken in the driver. | |||
| -- | |||
| 2.25.1 | |||
| From 974229575250290e49d004933cb0aade87fa4566 Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:33 +0000 | |||
| Subject: [PATCH 04/18] media: rkvdec: h264: Fix reference frame_num wrap for | |||
| second field | |||
| When decoding the second field in a complementary field pair the second | |||
| field is sharing the same frame_num with the first field. | |||
| Currently the frame_num for the first field is wrapped when it matches the | |||
| field being decoded, this cause issues to decode the second field in a | |||
| complementary field pair. | |||
| Fix this by using inclusive comparison, less than or equal. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| Reviewed-by: Ezequiel Garcia <ezequiel@collabora.com> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec-h264.c | 2 +- | |||
| 1 file changed, 1 insertion(+), 1 deletion(-) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| index 7b66e2743a4f..f0cfed84d60d 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| @@ -754,7 +754,7 @@ static void assemble_hw_rps(struct rkvdec_ctx *ctx, | |||
| continue; | |||
| if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM || | |||
| - dpb[i].frame_num < sl_params->frame_num) { | |||
| + dpb[i].frame_num <= sl_params->frame_num) { | |||
| p[i] = dpb[i].frame_num; | |||
| continue; | |||
| } | |||
| -- | |||
| 2.25.1 | |||
| From 031ff8b97b208e20b49554c657e8ca9c79a079db Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:34 +0000 | |||
| Subject: [PATCH 05/18] media: rkvdec: Ensure decoded resolution fit coded | |||
| resolution | |||
| Ensure decoded CAPTURE buffer resolution is larger or equal to the coded | |||
| OPTUPT buffer resolution. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec.c | 2 ++ | |||
| 1 file changed, 2 insertions(+) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c | |||
| index 55dc27171ce4..4ab8f7e0566b 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec.c | |||
| @@ -235,6 +235,8 @@ static int rkvdec_try_capture_fmt(struct file *file, void *priv, | |||
| pix_mp->pixelformat = coded_desc->decoded_fmts[0]; | |||
| /* Always apply the frmsize constraint of the coded end. */ | |||
| + pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width); | |||
| + pix_mp->height = max(pix_mp->height, ctx->coded_fmt.fmt.pix_mp.height); | |||
| v4l2_apply_frmsize_constraints(&pix_mp->width, | |||
| &pix_mp->height, | |||
| &coded_desc->frmsize); | |||
| -- | |||
| 2.25.1 | |||
| From 4c7271baa21e2e508593c8e87d77febff68afb6a Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:34 +0000 | |||
| Subject: [PATCH 06/18] media: rkvdec: h264: Validate and use pic width and | |||
| height in mbs | |||
| The width and height in mbs is currently configured based on OUTPUT buffer | |||
| resolution, this works for frame pictures but can cause issues for field | |||
| pictures. | |||
| When frame_mbs_only_flag is 0 the height in mbs should be height of | |||
| the field instead of height of frame. | |||
| Validate pic_width_in_mbs_minus1 and pic_height_in_map_units_minus1 | |||
| against OUTPUT buffer resolution and use these values to configure HW. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec-h264.c | 4 ++-- | |||
| drivers/staging/media/rkvdec/rkvdec.c | 10 ++++++++++ | |||
| 2 files changed, 12 insertions(+), 2 deletions(-) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| index f0cfed84d60d..3498e9eec3d8 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| @@ -672,8 +672,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, | |||
| LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4); | |||
| WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO), | |||
| DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG); | |||
| - WRITE_PPS(DIV_ROUND_UP(ctx->coded_fmt.fmt.pix_mp.width, 16), PIC_WIDTH_IN_MBS); | |||
| - WRITE_PPS(DIV_ROUND_UP(ctx->coded_fmt.fmt.pix_mp.height, 16), PIC_HEIGHT_IN_MBS); | |||
| + WRITE_PPS(sps->pic_width_in_mbs_minus1 + 1, PIC_WIDTH_IN_MBS); | |||
| + WRITE_PPS(sps->pic_height_in_map_units_minus1 + 1, PIC_HEIGHT_IN_MBS); | |||
| WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY), | |||
| FRAME_MBS_ONLY_FLAG); | |||
| WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD), | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c | |||
| index 4ab8f7e0566b..7a9f78bc0a55 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec.c | |||
| @@ -29,8 +29,11 @@ | |||
| static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) | |||
| { | |||
| + struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); | |||
| + | |||
| if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { | |||
| const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p; | |||
| + unsigned int width, height; | |||
| /* | |||
| * TODO: The hardware supports 10-bit and 4:2:2 profiles, | |||
| * but it's currently broken in the driver. | |||
| @@ -45,6 +48,13 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) | |||
| if (sps->bit_depth_luma_minus8 != 0) | |||
| /* Only 8-bit is supported */ | |||
| return -EINVAL; | |||
| + | |||
| + width = (sps->pic_width_in_mbs_minus1 + 1) * 16; | |||
| + height = (sps->pic_height_in_map_units_minus1 + 1) * 16; | |||
| + | |||
| + if (width > ctx->coded_fmt.fmt.pix_mp.width || | |||
| + height > ctx->coded_fmt.fmt.pix_mp.height) | |||
| + return -EINVAL; | |||
| } | |||
| return 0; | |||
| } | |||
| -- | |||
| 2.25.1 | |||
| From d0673c50f58c79198b3c2958beaa8b88e2bdfaef Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:35 +0000 | |||
| Subject: [PATCH 07/18] media: rkvdec: h264: Fix bit depth wrap in pps packet | |||
| The luma and chroma bit depth fields in the pps packet is 3 bits wide. | |||
| 8 is wrongly added to the bit depth value written to these 3-bit fields. | |||
| Because only the 3 LSB is written the hardware is configured correctly. | |||
| Correct this by not adding 8 to the luma and chroma bit depth value. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| Reviewed-by: Ezequiel Garcia <ezequiel@collabora.com> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec-h264.c | 4 ++-- | |||
| 1 file changed, 2 insertions(+), 2 deletions(-) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| index 3498e9eec3d8..6576b4a101ae 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| @@ -662,8 +662,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, | |||
| WRITE_PPS(0xff, PROFILE_IDC); | |||
| WRITE_PPS(1, CONSTRAINT_SET3_FLAG); | |||
| WRITE_PPS(sps->chroma_format_idc, CHROMA_FORMAT_IDC); | |||
| - WRITE_PPS(sps->bit_depth_luma_minus8 + 8, BIT_DEPTH_LUMA); | |||
| - WRITE_PPS(sps->bit_depth_chroma_minus8 + 8, BIT_DEPTH_CHROMA); | |||
| + WRITE_PPS(sps->bit_depth_luma_minus8, BIT_DEPTH_LUMA); | |||
| + WRITE_PPS(sps->bit_depth_chroma_minus8, BIT_DEPTH_CHROMA); | |||
| WRITE_PPS(0, QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG); | |||
| WRITE_PPS(sps->log2_max_frame_num_minus4, LOG2_MAX_FRAME_NUM_MINUS4); | |||
| WRITE_PPS(sps->max_num_ref_frames, MAX_NUM_REF_FRAMES); | |||
| -- | |||
| 2.25.1 | |||
| From c1cb24d82d654583f9b716f8385254d61223aa03 Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:35 +0000 | |||
| Subject: [PATCH 08/18] media: rkvdec: h264: Do not override output buffer | |||
| sizeimage | |||
| The output buffer sizeimage is currently forced to 2 bytes per pixel, this | |||
| can lead to high memory usage for 4K content when multiple output buffers | |||
| is created by userspace. | |||
| Do not override output buffer sizeimage and let userspace have control of | |||
| output buffer sizeimage by only setting sizeimage if none is provided. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec-h264.c | 5 +++-- | |||
| 1 file changed, 3 insertions(+), 2 deletions(-) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| index 6576b4a101ae..3a85545bcb38 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| @@ -1012,8 +1012,9 @@ static int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, | |||
| struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; | |||
| fmt->num_planes = 1; | |||
| - fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * | |||
| - RKVDEC_H264_MAX_DEPTH_IN_BYTES; | |||
| + if (!fmt->plane_fmt[0].sizeimage) | |||
| + fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * | |||
| + RKVDEC_H264_MAX_DEPTH_IN_BYTES; | |||
| return 0; | |||
| } | |||
| -- | |||
| 2.25.1 | |||
| From 1f381d3ddd88174c12846ba93ddd7deb47089a8e Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:35 +0000 | |||
| Subject: [PATCH 09/18] media: v4l2-common: Add helpers to calculate | |||
| bytesperline and sizeimage | |||
| Add helper functions to calculate plane bytesperline and sizeimage, these | |||
| new helpers consider block width and height when calculating plane | |||
| bytesperline and sizeimage. | |||
| This prepare support for new pixel formats added in next patch that make | |||
| use of block width and height. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| --- | |||
| drivers/media/v4l2-core/v4l2-common.c | 77 +++++++++++++-------------- | |||
| 1 file changed, 38 insertions(+), 39 deletions(-) | |||
| diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c | |||
| index 3dc17ebe14fa..4102c373b48a 100644 | |||
| --- a/drivers/media/v4l2-core/v4l2-common.c | |||
| +++ b/drivers/media/v4l2-core/v4l2-common.c | |||
| @@ -333,6 +333,33 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf | |||
| return info->block_h[plane]; | |||
| } | |||
| +static inline unsigned int v4l2_format_plane_width(const struct v4l2_format_info *info, int plane, | |||
| + unsigned int width) | |||
| +{ | |||
| + unsigned int hdiv = plane ? info->hdiv : 1; | |||
| + unsigned int bytes = DIV_ROUND_UP(width * info->bpp[plane], | |||
| + v4l2_format_block_width(info, plane) * | |||
| + v4l2_format_block_height(info, plane)); | |||
| + | |||
| + return DIV_ROUND_UP(bytes, hdiv); | |||
| +} | |||
| + | |||
| +static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_info *info, int plane, | |||
| + unsigned int height) | |||
| +{ | |||
| + unsigned int vdiv = plane ? info->vdiv : 1; | |||
| + unsigned int lines = ALIGN(height, v4l2_format_block_height(info, plane)); | |||
| + | |||
| + return DIV_ROUND_UP(lines, vdiv); | |||
| +} | |||
| + | |||
| +static inline unsigned int v4l2_format_plane_size(const struct v4l2_format_info *info, int plane, | |||
| + unsigned int width, unsigned int height) | |||
| +{ | |||
| + return v4l2_format_plane_width(info, plane, width) * | |||
| + v4l2_format_plane_height(info, plane, height); | |||
| +} | |||
| + | |||
| void v4l2_apply_frmsize_constraints(u32 *width, u32 *height, | |||
| const struct v4l2_frmsize_stepwise *frmsize) | |||
| { | |||
| @@ -368,37 +395,19 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, | |||
| if (info->mem_planes == 1) { | |||
| plane = &pixfmt->plane_fmt[0]; | |||
| - plane->bytesperline = ALIGN(width, v4l2_format_block_width(info, 0)) * info->bpp[0]; | |||
| + plane->bytesperline = v4l2_format_plane_width(info, 0, width); | |||
| plane->sizeimage = 0; | |||
| - for (i = 0; i < info->comp_planes; i++) { | |||
| - unsigned int hdiv = (i == 0) ? 1 : info->hdiv; | |||
| - unsigned int vdiv = (i == 0) ? 1 : info->vdiv; | |||
| - unsigned int aligned_width; | |||
| - unsigned int aligned_height; | |||
| - | |||
| - aligned_width = ALIGN(width, v4l2_format_block_width(info, i)); | |||
| - aligned_height = ALIGN(height, v4l2_format_block_height(info, i)); | |||
| - | |||
| - plane->sizeimage += info->bpp[i] * | |||
| - DIV_ROUND_UP(aligned_width, hdiv) * | |||
| - DIV_ROUND_UP(aligned_height, vdiv); | |||
| - } | |||
| + for (i = 0; i < info->comp_planes; i++) | |||
| + plane->sizeimage += | |||
| + v4l2_format_plane_size(info, i, width, height); | |||
| } else { | |||
| for (i = 0; i < info->comp_planes; i++) { | |||
| - unsigned int hdiv = (i == 0) ? 1 : info->hdiv; | |||
| - unsigned int vdiv = (i == 0) ? 1 : info->vdiv; | |||
| - unsigned int aligned_width; | |||
| - unsigned int aligned_height; | |||
| - | |||
| - aligned_width = ALIGN(width, v4l2_format_block_width(info, i)); | |||
| - aligned_height = ALIGN(height, v4l2_format_block_height(info, i)); | |||
| - | |||
| plane = &pixfmt->plane_fmt[i]; | |||
| plane->bytesperline = | |||
| - info->bpp[i] * DIV_ROUND_UP(aligned_width, hdiv); | |||
| - plane->sizeimage = | |||
| - plane->bytesperline * DIV_ROUND_UP(aligned_height, vdiv); | |||
| + v4l2_format_plane_width(info, i, width); | |||
| + plane->sizeimage = plane->bytesperline * | |||
| + v4l2_format_plane_height(info, i, height); | |||
| } | |||
| } | |||
| return 0; | |||
| @@ -422,22 +431,12 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, | |||
| pixfmt->width = width; | |||
| pixfmt->height = height; | |||
| pixfmt->pixelformat = pixelformat; | |||
| - pixfmt->bytesperline = ALIGN(width, v4l2_format_block_width(info, 0)) * info->bpp[0]; | |||
| + pixfmt->bytesperline = v4l2_format_plane_width(info, 0, width); | |||
| pixfmt->sizeimage = 0; | |||
| - for (i = 0; i < info->comp_planes; i++) { | |||
| - unsigned int hdiv = (i == 0) ? 1 : info->hdiv; | |||
| - unsigned int vdiv = (i == 0) ? 1 : info->vdiv; | |||
| - unsigned int aligned_width; | |||
| - unsigned int aligned_height; | |||
| - | |||
| - aligned_width = ALIGN(width, v4l2_format_block_width(info, i)); | |||
| - aligned_height = ALIGN(height, v4l2_format_block_height(info, i)); | |||
| - | |||
| - pixfmt->sizeimage += info->bpp[i] * | |||
| - DIV_ROUND_UP(aligned_width, hdiv) * | |||
| - DIV_ROUND_UP(aligned_height, vdiv); | |||
| - } | |||
| + for (i = 0; i < info->comp_planes; i++) | |||
| + pixfmt->sizeimage += | |||
| + v4l2_format_plane_size(info, i, width, height); | |||
| return 0; | |||
| } | |||
| EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt); | |||
| -- | |||
| 2.25.1 | |||
| From 778ba4f6cb10b7e4ae2dd71c35dcdd2363d43b21 Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:36 +0000 | |||
| Subject: [PATCH 10/18] media: v4l2: Add NV15 and NV20 pixel formats | |||
| Add NV15 and NV20 pixel formats used by the Rockchip Video Decoder for | |||
| 10-bit buffers. | |||
| NV15 and NV20 is a packed 10-bit 4:2:0/4:2:2 semi-planar Y/CbCr format | |||
| similar to P010 and P210 but has no padding between components. Instead, | |||
| luminance and chrominance samples are grouped into 4s so that each group is | |||
| packed into an integer number of bytes: | |||
| YYYY = UVUV = 4 * 10 bits = 40 bits = 5 bytes | |||
| The '15' and '20' suffix refers to the optimum effective bits per pixel | |||
| which is achieved when the total number of luminance samples is a multiple | |||
| of 8 for NV15 and 4 for NV20. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| --- | |||
| .../userspace-api/media/v4l/pixfmt-nv15.rst | 101 ++++++++++++++++++ | |||
| .../userspace-api/media/v4l/pixfmt-nv20.rst | 99 +++++++++++++++++ | |||
| .../userspace-api/media/v4l/yuv-formats.rst | 2 + | |||
| drivers/media/v4l2-core/v4l2-common.c | 3 + | |||
| drivers/media/v4l2-core/v4l2-ioctl.c | 2 + | |||
| include/uapi/linux/videodev2.h | 3 + | |||
| 6 files changed, 210 insertions(+) | |||
| create mode 100644 Documentation/userspace-api/media/v4l/pixfmt-nv15.rst | |||
| create mode 100644 Documentation/userspace-api/media/v4l/pixfmt-nv20.rst | |||
| diff --git a/Documentation/userspace-api/media/v4l/pixfmt-nv15.rst b/Documentation/userspace-api/media/v4l/pixfmt-nv15.rst | |||
| new file mode 100644 | |||
| index 000000000000..d059db58c6e0 | |||
| --- /dev/null | |||
| +++ b/Documentation/userspace-api/media/v4l/pixfmt-nv15.rst | |||
| @@ -0,0 +1,101 @@ | |||
| +.. Permission is granted to copy, distribute and/or modify this | |||
| +.. document under the terms of the GNU Free Documentation License, | |||
| +.. Version 1.1 or any later version published by the Free Software | |||
| +.. Foundation, with no Invariant Sections, no Front-Cover Texts | |||
| +.. and no Back-Cover Texts. A copy of the license is included at | |||
| +.. Documentation/userspace-api/media/fdl-appendix.rst. | |||
| +.. | |||
| +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections | |||
| + | |||
| +.. _V4L2-PIX-FMT-NV15: | |||
| + | |||
| +************************** | |||
| +V4L2_PIX_FMT_NV15 ('NV15') | |||
| +************************** | |||
| + | |||
| +Format with ½ horizontal and vertical chroma resolution, also known as | |||
| +YUV 4:2:0. One luminance and one chrominance plane with alternating | |||
| +chroma samples similar to ``V4L2_PIX_FMT_NV12`` but with 10-bit samples | |||
| +that are grouped into four and packed into five bytes. | |||
| + | |||
| +The '15' suffix refers to the optimum effective bits per pixel which is | |||
| +achieved when the total number of luminance samples is a multiple of 8. | |||
| + | |||
| + | |||
| +Description | |||
| +=========== | |||
| + | |||
| +This is a packed 10-bit two-plane version of the YUV 4:2:0 format. The | |||
| +three components are separated into two sub-images or planes. The Y plane | |||
| +is first. The Y plane has five bytes per each group of four pixels. A | |||
| +combined CbCr plane immediately follows the Y plane in memory. The CbCr | |||
| +plane is the same width, in bytes, as the Y plane (and of the image), but | |||
| +is half as tall in pixels. Each CbCr pair belongs to four pixels. For | |||
| +example, Cb\ :sub:`00`/Cr\ :sub:`00` belongs to Y'\ :sub:`00`, | |||
| +Y'\ :sub:`01`, Y'\ :sub:`10`, Y'\ :sub:`11`. | |||
| + | |||
| +If the Y plane has pad bytes after each row, then the CbCr plane has as | |||
| +many pad bytes after its rows. | |||
| + | |||
| +**Byte Order.** | |||
| +Little endian. Each cell is one byte. Pixels cross the byte boundary. | |||
| + | |||
| + | |||
| +.. flat-table:: | |||
| + :header-rows: 0 | |||
| + :stub-columns: 0 | |||
| + | |||
| + * - start + 0: | |||
| + - Y'\ :sub:`00[7:0]` | |||
| + - Y'\ :sub:`01[5:0]`\ Y'\ :sub:`00[9:8]` | |||
| + - Y'\ :sub:`02[3:0]`\ Y'\ :sub:`01[9:6]` | |||
| + - Y'\ :sub:`03[1:0]`\ Y'\ :sub:`02[9:4]` | |||
| + - Y'\ :sub:`03[9:2]` | |||
| + * - start + 5: | |||
| + - Y'\ :sub:`10[7:0]` | |||
| + - Y'\ :sub:`11[5:0]`\ Y'\ :sub:`10[9:8]` | |||
| + - Y'\ :sub:`12[3:0]`\ Y'\ :sub:`11[9:6]` | |||
| + - Y'\ :sub:`13[1:0]`\ Y'\ :sub:`12[9:4]` | |||
| + - Y'\ :sub:`13[9:2]` | |||
| + * - start + 10: | |||
| + - Cb'\ :sub:`00[7:0]` | |||
| + - Cr'\ :sub:`00[5:0]`\ Cb'\ :sub:`00[9:8]` | |||
| + - Cb'\ :sub:`01[3:0]`\ Cr'\ :sub:`00[9:6]` | |||
| + - Cr'\ :sub:`01[1:0]`\ Cb'\ :sub:`01[9:4]` | |||
| + - Cr'\ :sub:`01[9:2]` | |||
| + | |||
| + | |||
| +**Color Sample Location:** | |||
| + | |||
| +.. flat-table:: | |||
| + :header-rows: 0 | |||
| + :stub-columns: 0 | |||
| + | |||
| + * - | |||
| + - 0 | |||
| + - | |||
| + - 1 | |||
| + - 2 | |||
| + - | |||
| + - 3 | |||
| + * - 0 | |||
| + - Y | |||
| + - | |||
| + - Y | |||
| + - Y | |||
| + - | |||
| + - Y | |||
| + * - | |||
| + - | |||
| + - C | |||
| + - | |||
| + - | |||
| + - C | |||
| + - | |||
| + * - 1 | |||
| + - Y | |||
| + - | |||
| + - Y | |||
| + - Y | |||
| + - | |||
| + - Y | |||
| diff --git a/Documentation/userspace-api/media/v4l/pixfmt-nv20.rst b/Documentation/userspace-api/media/v4l/pixfmt-nv20.rst | |||
| new file mode 100644 | |||
| index 000000000000..a8123be0baa3 | |||
| --- /dev/null | |||
| +++ b/Documentation/userspace-api/media/v4l/pixfmt-nv20.rst | |||
| @@ -0,0 +1,99 @@ | |||
| +.. Permission is granted to copy, distribute and/or modify this | |||
| +.. document under the terms of the GNU Free Documentation License, | |||
| +.. Version 1.1 or any later version published by the Free Software | |||
| +.. Foundation, with no Invariant Sections, no Front-Cover Texts | |||
| +.. and no Back-Cover Texts. A copy of the license is included at | |||
| +.. Documentation/userspace-api/media/fdl-appendix.rst. | |||
| +.. | |||
| +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections | |||
| + | |||
| +.. _V4L2-PIX-FMT-NV20: | |||
| + | |||
| +************************** | |||
| +V4L2_PIX_FMT_NV20 ('NV20') | |||
| +************************** | |||
| + | |||
| +Format with ½ horizontal chroma resolution, also known as YUV 4:2:2. | |||
| +One luminance and one chrominance plane with alternating chroma samples | |||
| +similar to ``V4L2_PIX_FMT_NV16`` but with 10-bit samples | |||
| +that are grouped into four and packed into five bytes. | |||
| + | |||
| +The '20' suffix refers to the optimum effective bits per pixel which is | |||
| +achieved when the total number of luminance samples is a multiple of 4. | |||
| + | |||
| + | |||
| +Description | |||
| +=========== | |||
| + | |||
| +This is a packed 10-bit two-plane version of the YUV 4:2:2 format. The | |||
| +three components are separated into two sub-images or planes. The Y plane | |||
| +is first. The Y plane has five bytes per each group of four pixels. A | |||
| +combined CbCr plane immediately follows the Y plane in memory. The CbCr | |||
| +plane is the same width and height, in bytes, as the Y plane (and of the | |||
| +image). Each CbCr pair belongs to two pixels. For example, | |||
| +Cb\ :sub:`00`/Cr\ :sub:`00` belongs to Y'\ :sub:`00`, Y'\ :sub:`01`. | |||
| + | |||
| +If the Y plane has pad bytes after each row, then the CbCr plane has as | |||
| +many pad bytes after its rows. | |||
| + | |||
| +**Byte Order.** | |||
| +Little endian. Each cell is one byte. Pixels cross the byte boundary. | |||
| + | |||
| + | |||
| +.. flat-table:: | |||
| + :header-rows: 0 | |||
| + :stub-columns: 0 | |||
| + | |||
| + * - start + 0: | |||
| + - Y'\ :sub:`00[7:0]` | |||
| + - Y'\ :sub:`01[5:0]`\ Y'\ :sub:`00[9:8]` | |||
| + - Y'\ :sub:`02[3:0]`\ Y'\ :sub:`01[9:6]` | |||
| + - Y'\ :sub:`03[1:0]`\ Y'\ :sub:`02[9:4]` | |||
| + - Y'\ :sub:`03[9:2]` | |||
| + * - start + 5: | |||
| + - Y'\ :sub:`10[7:0]` | |||
| + - Y'\ :sub:`11[5:0]`\ Y'\ :sub:`10[9:8]` | |||
| + - Y'\ :sub:`12[3:0]`\ Y'\ :sub:`11[9:6]` | |||
| + - Y'\ :sub:`13[1:0]`\ Y'\ :sub:`12[9:4]` | |||
| + - Y'\ :sub:`13[9:2]` | |||
| + * - start + 10: | |||
| + - Cb'\ :sub:`00[7:0]` | |||
| + - Cr'\ :sub:`00[5:0]`\ Cb'\ :sub:`00[9:8]` | |||
| + - Cb'\ :sub:`01[3:0]`\ Cr'\ :sub:`00[9:6]` | |||
| + - Cr'\ :sub:`01[1:0]`\ Cb'\ :sub:`01[9:4]` | |||
| + - Cr'\ :sub:`01[9:2]` | |||
| + * - start + 15: | |||
| + - Cb'\ :sub:`10[7:0]` | |||
| + - Cr'\ :sub:`10[5:0]`\ Cb'\ :sub:`10[9:8]` | |||
| + - Cb'\ :sub:`11[3:0]`\ Cr'\ :sub:`10[9:6]` | |||
| + - Cr'\ :sub:`11[1:0]`\ Cb'\ :sub:`11[9:4]` | |||
| + - Cr'\ :sub:`11[9:2]` | |||
| + | |||
| + | |||
| +**Color Sample Location:** | |||
| + | |||
| +.. flat-table:: | |||
| + :header-rows: 0 | |||
| + :stub-columns: 0 | |||
| + | |||
| + * - | |||
| + - 0 | |||
| + - | |||
| + - 1 | |||
| + - 2 | |||
| + - | |||
| + - 3 | |||
| + * - 0 | |||
| + - Y | |||
| + - C | |||
| + - Y | |||
| + - Y | |||
| + - C | |||
| + - Y | |||
| + * - 1 | |||
| + - Y | |||
| + - C | |||
| + - Y | |||
| + - Y | |||
| + - C | |||
| + - Y | |||
| diff --git a/Documentation/userspace-api/media/v4l/yuv-formats.rst b/Documentation/userspace-api/media/v4l/yuv-formats.rst | |||
| index 8ee92d0cd769..7cca883f178a 100644 | |||
| --- a/Documentation/userspace-api/media/v4l/yuv-formats.rst | |||
| +++ b/Documentation/userspace-api/media/v4l/yuv-formats.rst | |||
| @@ -61,4 +61,6 @@ to brightness information. | |||
| pixfmt-nv16 | |||
| pixfmt-nv16m | |||
| pixfmt-nv24 | |||
| + pixfmt-nv15 | |||
| + pixfmt-nv20 | |||
| pixfmt-m420 | |||
| diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c | |||
| index 4102c373b48a..0caac755d303 100644 | |||
| --- a/drivers/media/v4l2-core/v4l2-common.c | |||
| +++ b/drivers/media/v4l2-core/v4l2-common.c | |||
| @@ -267,6 +267,9 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) | |||
| { .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 }, | |||
| { .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 }, | |||
| + { .format = V4L2_PIX_FMT_NV15, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 5, 0, 0 }, .hdiv = 2, .vdiv = 2, .block_w = { 4, 2, 0, 0 }, .block_h = { 1, 1, 0, 0 } }, | |||
| + { .format = V4L2_PIX_FMT_NV20, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 5, 0, 0 }, .hdiv = 2, .vdiv = 1, .block_w = { 4, 2, 0, 0 }, .block_h = { 1, 1, 0, 0 } }, | |||
| + | |||
| { .format = V4L2_PIX_FMT_YUV410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 }, | |||
| { .format = V4L2_PIX_FMT_YVU410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 }, | |||
| { .format = V4L2_PIX_FMT_YUV411P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 1 }, | |||
| diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c | |||
| index 02bfef0da76d..4657274bb37a 100644 | |||
| --- a/drivers/media/v4l2-core/v4l2-ioctl.c | |||
| +++ b/drivers/media/v4l2-core/v4l2-ioctl.c | |||
| @@ -1315,6 +1315,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) | |||
| case V4L2_PIX_FMT_NV61: descr = "Y/CrCb 4:2:2"; break; | |||
| case V4L2_PIX_FMT_NV24: descr = "Y/CbCr 4:4:4"; break; | |||
| case V4L2_PIX_FMT_NV42: descr = "Y/CrCb 4:4:4"; break; | |||
| + case V4L2_PIX_FMT_NV15: descr = "10-bit Y/CbCr 4:2:0 (Packed)"; break; | |||
| + case V4L2_PIX_FMT_NV20: descr = "10-bit Y/CbCr 4:2:2 (Packed)"; break; | |||
| case V4L2_PIX_FMT_NV12M: descr = "Y/CbCr 4:2:0 (N-C)"; break; | |||
| case V4L2_PIX_FMT_NV21M: descr = "Y/CrCb 4:2:0 (N-C)"; break; | |||
| case V4L2_PIX_FMT_NV16M: descr = "Y/CbCr 4:2:2 (N-C)"; break; | |||
| diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h | |||
| index 303805438814..bd23aeaf0706 100644 | |||
| --- a/include/uapi/linux/videodev2.h | |||
| +++ b/include/uapi/linux/videodev2.h | |||
| @@ -610,6 +610,9 @@ struct v4l2_pix_format { | |||
| #define V4L2_PIX_FMT_NV24 v4l2_fourcc('N', 'V', '2', '4') /* 24 Y/CbCr 4:4:4 */ | |||
| #define V4L2_PIX_FMT_NV42 v4l2_fourcc('N', 'V', '4', '2') /* 24 Y/CrCb 4:4:4 */ | |||
| +#define V4L2_PIX_FMT_NV15 v4l2_fourcc('N', 'V', '1', '5') /* 15 Y/CbCr 4:2:0 10-bit packed */ | |||
| +#define V4L2_PIX_FMT_NV20 v4l2_fourcc('N', 'V', '2', '0') /* 20 Y/CbCr 4:2:2 10-bit packed */ | |||
| + | |||
| /* two non contiguous planes - one Y, one Cr + Cb interleaved */ | |||
| #define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */ | |||
| #define V4L2_PIX_FMT_NV21M v4l2_fourcc('N', 'M', '2', '1') /* 21 Y/CrCb 4:2:0 */ | |||
| -- | |||
| 2.25.1 | |||
| From ac18ccac6e1a05ecdffd1eb5e8017d92a5eb6c0f Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:36 +0000 | |||
| Subject: [PATCH 11/18] media: rkvdec: h264: Use bytesperline and buffer height | |||
| to calculate stride | |||
| Use bytesperline and buffer height to calculate the strides configured. | |||
| This does not really change anything other than ensuring the bytesperline | |||
| that is signaled to userspace matches what is configured in HW. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec-h264.c | 10 +++++----- | |||
| 1 file changed, 5 insertions(+), 5 deletions(-) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| index 3a85545bcb38..10756b9d6118 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| @@ -891,9 +891,9 @@ static void config_registers(struct rkvdec_ctx *ctx, | |||
| dma_addr_t rlc_addr; | |||
| dma_addr_t refer_addr; | |||
| u32 rlc_len; | |||
| - u32 hor_virstride = 0; | |||
| - u32 ver_virstride = 0; | |||
| - u32 y_virstride = 0; | |||
| + u32 hor_virstride; | |||
| + u32 ver_virstride; | |||
| + u32 y_virstride; | |||
| u32 yuv_virstride = 0; | |||
| u32 offset; | |||
| dma_addr_t dst_addr; | |||
| @@ -904,8 +904,8 @@ static void config_registers(struct rkvdec_ctx *ctx, | |||
| f = &ctx->decoded_fmt; | |||
| dst_fmt = &f->fmt.pix_mp; | |||
| - hor_virstride = (sps->bit_depth_luma_minus8 + 8) * dst_fmt->width / 8; | |||
| - ver_virstride = round_up(dst_fmt->height, 16); | |||
| + hor_virstride = dst_fmt->plane_fmt[0].bytesperline; | |||
| + ver_virstride = dst_fmt->height; | |||
| y_virstride = hor_virstride * ver_virstride; | |||
| if (sps->chroma_format_idc == 0) | |||
| -- | |||
| 2.25.1 | |||
| From 62e313c4f82da894647a166a923c32746c2715ad Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:37 +0000 | |||
| Subject: [PATCH 12/18] media: rkvdec: Extract rkvdec_fill_decoded_pixfmt | |||
| helper method | |||
| This extract setting decoded pixfmt into a helper method, current code is | |||
| replaced with a call to the new helper method. | |||
| The helper method is also called from a new function in next patch. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec.c | 29 ++++++++++++++------------- | |||
| 1 file changed, 15 insertions(+), 14 deletions(-) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c | |||
| index 7a9f78bc0a55..911132be6d50 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec.c | |||
| @@ -27,6 +27,17 @@ | |||
| #include "rkvdec.h" | |||
| #include "rkvdec-regs.h" | |||
| +static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, | |||
| + struct v4l2_pix_format_mplane *pix_mp) | |||
| +{ | |||
| + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, | |||
| + pix_mp->width, pix_mp->height); | |||
| + pix_mp->plane_fmt[0].sizeimage += 128 * | |||
| + DIV_ROUND_UP(pix_mp->width, 16) * | |||
| + DIV_ROUND_UP(pix_mp->height, 16); | |||
| + pix_mp->field = V4L2_FIELD_NONE; | |||
| +} | |||
| + | |||
| static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) | |||
| { | |||
| struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); | |||
| @@ -179,13 +190,9 @@ static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx) | |||
| rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]); | |||
| f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |||
| - v4l2_fill_pixfmt_mp(&f->fmt.pix_mp, | |||
| - ctx->coded_fmt_desc->decoded_fmts[0], | |||
| - ctx->coded_fmt.fmt.pix_mp.width, | |||
| - ctx->coded_fmt.fmt.pix_mp.height); | |||
| - f->fmt.pix_mp.plane_fmt[0].sizeimage += 128 * | |||
| - DIV_ROUND_UP(f->fmt.pix_mp.width, 16) * | |||
| - DIV_ROUND_UP(f->fmt.pix_mp.height, 16); | |||
| + f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; | |||
| + f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height; | |||
| + rkvdec_fill_decoded_pixfmt(ctx, &f->fmt.pix_mp); | |||
| } | |||
| static int rkvdec_enum_framesizes(struct file *file, void *priv, | |||
| @@ -251,13 +258,7 @@ static int rkvdec_try_capture_fmt(struct file *file, void *priv, | |||
| &pix_mp->height, | |||
| &coded_desc->frmsize); | |||
| - v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, | |||
| - pix_mp->width, pix_mp->height); | |||
| - pix_mp->plane_fmt[0].sizeimage += | |||
| - 128 * | |||
| - DIV_ROUND_UP(pix_mp->width, 16) * | |||
| - DIV_ROUND_UP(pix_mp->height, 16); | |||
| - pix_mp->field = V4L2_FIELD_NONE; | |||
| + rkvdec_fill_decoded_pixfmt(ctx, pix_mp); | |||
| return 0; | |||
| } | |||
| -- | |||
| 2.25.1 | |||
| From ce2a49e0b4908dbc148df81a053bb3eaf8c28048 Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:37 +0000 | |||
| Subject: [PATCH 13/18] media: rkvdec: Lock capture pixel format in s_ctrl and | |||
| s_fmt | |||
| Add an optional valid_fmt operation that should return the valid | |||
| pixelformat of CAPTURE buffers. | |||
| This is used in next patch to ensure correct pixelformat is used for 10-bit | |||
| and 4:2:2 content. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec.c | 59 ++++++++++++++++++++++++--- | |||
| drivers/staging/media/rkvdec/rkvdec.h | 2 + | |||
| 2 files changed, 55 insertions(+), 6 deletions(-) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c | |||
| index 911132be6d50..11a88cb6407d 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec.c | |||
| @@ -38,6 +38,16 @@ static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, | |||
| pix_mp->field = V4L2_FIELD_NONE; | |||
| } | |||
| +static u32 rkvdec_valid_fmt(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl) | |||
| +{ | |||
| + const struct rkvdec_coded_fmt_desc *coded_desc = ctx->coded_fmt_desc; | |||
| + | |||
| + if (coded_desc->ops->valid_fmt) | |||
| + return coded_desc->ops->valid_fmt(ctx, ctrl); | |||
| + | |||
| + return ctx->valid_fmt; | |||
| +} | |||
| + | |||
| static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) | |||
| { | |||
| struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); | |||
| @@ -60,6 +70,10 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) | |||
| /* Only 8-bit is supported */ | |||
| return -EINVAL; | |||
| + if (ctx->valid_fmt && ctx->valid_fmt != rkvdec_valid_fmt(ctx, ctrl)) | |||
| + /* Only current valid format */ | |||
| + return -EINVAL; | |||
| + | |||
| width = (sps->pic_width_in_mbs_minus1 + 1) * 16; | |||
| height = (sps->pic_height_in_map_units_minus1 + 1) * 16; | |||
| @@ -70,8 +84,27 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) | |||
| return 0; | |||
| } | |||
| +static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl) | |||
| +{ | |||
| + struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); | |||
| + | |||
| + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS && !ctx->valid_fmt) { | |||
| + ctx->valid_fmt = rkvdec_valid_fmt(ctx, ctrl); | |||
| + if (ctx->valid_fmt) { | |||
| + struct v4l2_pix_format_mplane *pix_mp; | |||
| + | |||
| + pix_mp = &ctx->decoded_fmt.fmt.pix_mp; | |||
| + pix_mp->pixelformat = ctx->valid_fmt; | |||
| + rkvdec_fill_decoded_pixfmt(ctx, pix_mp); | |||
| + } | |||
| + } | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { | |||
| .try_ctrl = rkvdec_try_ctrl, | |||
| + .s_ctrl = rkvdec_s_ctrl, | |||
| }; | |||
| static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { | |||
| @@ -188,6 +221,7 @@ static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx) | |||
| { | |||
| struct v4l2_format *f = &ctx->decoded_fmt; | |||
| + ctx->valid_fmt = 0; | |||
| rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]); | |||
| f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |||
| f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; | |||
| @@ -243,13 +277,17 @@ static int rkvdec_try_capture_fmt(struct file *file, void *priv, | |||
| if (WARN_ON(!coded_desc)) | |||
| return -EINVAL; | |||
| - for (i = 0; i < coded_desc->num_decoded_fmts; i++) { | |||
| - if (coded_desc->decoded_fmts[i] == pix_mp->pixelformat) | |||
| - break; | |||
| - } | |||
| + if (ctx->valid_fmt) { | |||
| + pix_mp->pixelformat = ctx->valid_fmt; | |||
| + } else { | |||
| + for (i = 0; i < coded_desc->num_decoded_fmts; i++) { | |||
| + if (coded_desc->decoded_fmts[i] == pix_mp->pixelformat) | |||
| + break; | |||
| + } | |||
| - if (i == coded_desc->num_decoded_fmts) | |||
| - pix_mp->pixelformat = coded_desc->decoded_fmts[0]; | |||
| + if (i == coded_desc->num_decoded_fmts) | |||
| + pix_mp->pixelformat = coded_desc->decoded_fmts[0]; | |||
| + } | |||
| /* Always apply the frmsize constraint of the coded end. */ | |||
| pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width); | |||
| @@ -324,6 +362,7 @@ static int rkvdec_s_capture_fmt(struct file *file, void *priv, | |||
| return ret; | |||
| ctx->decoded_fmt = *f; | |||
| + ctx->valid_fmt = f->fmt.pix_mp.pixelformat; | |||
| return 0; | |||
| } | |||
| @@ -413,6 +452,14 @@ static int rkvdec_enum_capture_fmt(struct file *file, void *priv, | |||
| if (WARN_ON(!ctx->coded_fmt_desc)) | |||
| return -EINVAL; | |||
| + if (ctx->valid_fmt) { | |||
| + if (f->index) | |||
| + return -EINVAL; | |||
| + | |||
| + f->pixelformat = ctx->valid_fmt; | |||
| + return 0; | |||
| + } | |||
| + | |||
| if (f->index >= ctx->coded_fmt_desc->num_decoded_fmts) | |||
| return -EINVAL; | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec.h b/drivers/staging/media/rkvdec/rkvdec.h | |||
| index 2fc9f46b6910..50e67401fdbe 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec.h | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec.h | |||
| @@ -64,6 +64,7 @@ vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf) | |||
| struct rkvdec_coded_fmt_ops { | |||
| int (*adjust_fmt)(struct rkvdec_ctx *ctx, | |||
| struct v4l2_format *f); | |||
| + u32 (*valid_fmt)(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl); | |||
| int (*start)(struct rkvdec_ctx *ctx); | |||
| void (*stop)(struct rkvdec_ctx *ctx); | |||
| int (*run)(struct rkvdec_ctx *ctx); | |||
| @@ -97,6 +98,7 @@ struct rkvdec_ctx { | |||
| struct v4l2_fh fh; | |||
| struct v4l2_format coded_fmt; | |||
| struct v4l2_format decoded_fmt; | |||
| + u32 valid_fmt; | |||
| const struct rkvdec_coded_fmt_desc *coded_fmt_desc; | |||
| struct v4l2_ctrl_handler ctrl_hdl; | |||
| struct rkvdec_dev *dev; | |||
| -- | |||
| 2.25.1 | |||
| From 4a5ebc230d6f6ad93699631f2f55abb6d7bff43f Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:37 +0000 | |||
| Subject: [PATCH 14/18] media: rkvdec: h264: Support High 10 and 4:2:2 profiles | |||
| Add support and enable decoding of H264 High 10 and 4:2:2 profiles. | |||
| Decoded CAPTURE buffer width is aligned to 64 pixels to accommodate HW | |||
| requirement on 10-bit format buffers. | |||
| The new valid_fmt operation is implemented and return a valid pixelformat | |||
| for the provided SPS control. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec-h264.c | 20 ++++++++++++++++++++ | |||
| drivers/staging/media/rkvdec/rkvdec.c | 19 +++++++++---------- | |||
| 2 files changed, 29 insertions(+), 10 deletions(-) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| index 10756b9d6118..0757fc97d1ff 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec-h264.c | |||
| @@ -1018,6 +1018,25 @@ static int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, | |||
| return 0; | |||
| } | |||
| +static u32 rkvdec_h264_valid_fmt(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl) | |||
| +{ | |||
| + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p; | |||
| + | |||
| + if (sps->bit_depth_luma_minus8 == 0) { | |||
| + if (sps->chroma_format_idc == 2) | |||
| + return V4L2_PIX_FMT_NV16; | |||
| + else | |||
| + return V4L2_PIX_FMT_NV12; | |||
| + } else if (sps->bit_depth_luma_minus8 == 2) { | |||
| + if (sps->chroma_format_idc == 2) | |||
| + return V4L2_PIX_FMT_NV20; | |||
| + else | |||
| + return V4L2_PIX_FMT_NV15; | |||
| + } | |||
| + | |||
| + return 0; | |||
| +} | |||
| + | |||
| static int rkvdec_h264_start(struct rkvdec_ctx *ctx) | |||
| { | |||
| struct rkvdec_dev *rkvdec = ctx->dev; | |||
| @@ -1125,6 +1144,7 @@ static int rkvdec_h264_run(struct rkvdec_ctx *ctx) | |||
| const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops = { | |||
| .adjust_fmt = rkvdec_h264_adjust_fmt, | |||
| + .valid_fmt = rkvdec_h264_valid_fmt, | |||
| .start = rkvdec_h264_start, | |||
| .stop = rkvdec_h264_stop, | |||
| .run = rkvdec_h264_run, | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c | |||
| index 11a88cb6407d..4faee9262392 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec.c | |||
| @@ -31,7 +31,7 @@ static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, | |||
| struct v4l2_pix_format_mplane *pix_mp) | |||
| { | |||
| v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, | |||
| - pix_mp->width, pix_mp->height); | |||
| + ALIGN(pix_mp->width, 64), pix_mp->height); | |||
| pix_mp->plane_fmt[0].sizeimage += 128 * | |||
| DIV_ROUND_UP(pix_mp->width, 16) * | |||
| DIV_ROUND_UP(pix_mp->height, 16); | |||
| @@ -55,19 +55,15 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) | |||
| if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { | |||
| const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p; | |||
| unsigned int width, height; | |||
| - /* | |||
| - * TODO: The hardware supports 10-bit and 4:2:2 profiles, | |||
| - * but it's currently broken in the driver. | |||
| - * Reject them for now, until it's fixed. | |||
| - */ | |||
| - if (sps->chroma_format_idc > 1) | |||
| - /* Only 4:0:0 and 4:2:0 are supported */ | |||
| + | |||
| + if (sps->chroma_format_idc > 2) | |||
| + /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */ | |||
| return -EINVAL; | |||
| if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) | |||
| /* Luma and chroma bit depth mismatch */ | |||
| return -EINVAL; | |||
| - if (sps->bit_depth_luma_minus8 != 0) | |||
| - /* Only 8-bit is supported */ | |||
| + if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) | |||
| + /* Only 8-bit and 10-bit is supported */ | |||
| return -EINVAL; | |||
| if (ctx->valid_fmt && ctx->valid_fmt != rkvdec_valid_fmt(ctx, ctrl)) | |||
| @@ -157,6 +153,9 @@ static const struct rkvdec_ctrls rkvdec_h264_ctrls = { | |||
| static const u32 rkvdec_h264_decoded_fmts[] = { | |||
| V4L2_PIX_FMT_NV12, | |||
| + V4L2_PIX_FMT_NV15, | |||
| + V4L2_PIX_FMT_NV16, | |||
| + V4L2_PIX_FMT_NV20, | |||
| }; | |||
| static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { | |||
| -- | |||
| 2.25.1 | |||
| From 4dc94c5df4663d2120faaddfe1c581b4c3f347b9 Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 21:54:38 +0000 | |||
| Subject: [PATCH 15/18] media: rkvdec: h264: Support profile and level controls | |||
| The Rockchip Video Decoder used in RK3399 supports H.264 profiles from | |||
| Baseline to High 4:2:2 up to Level 5.1, except for the Extended profile. | |||
| Expose the V4L2_CID_MPEG_VIDEO_H264_PROFILE and the | |||
| V4L2_CID_MPEG_VIDEO_H264_LEVEL control, so that userspace can query the | |||
| driver for the list of supported profiles and level. | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| Reviewed-by: Ezequiel Garcia <ezequiel@collabora.com> | |||
| --- | |||
| drivers/staging/media/rkvdec/rkvdec.c | 13 +++++++++++++ | |||
| 1 file changed, 13 insertions(+) | |||
| diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c | |||
| index 4faee9262392..b21031535330 100644 | |||
| --- a/drivers/staging/media/rkvdec/rkvdec.c | |||
| +++ b/drivers/staging/media/rkvdec/rkvdec.c | |||
| @@ -144,6 +144,19 @@ static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { | |||
| .cfg.def = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, | |||
| .cfg.max = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, | |||
| }, | |||
| + { | |||
| + .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, | |||
| + .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, | |||
| + .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422, | |||
| + .cfg.menu_skip_mask = | |||
| + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), | |||
| + .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, | |||
| + }, | |||
| + { | |||
| + .cfg.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, | |||
| + .cfg.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, | |||
| + .cfg.max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, | |||
| + }, | |||
| }; | |||
| static const struct rkvdec_ctrls rkvdec_h264_ctrls = { | |||
| -- | |||
| 2.25.1 | |||
| From 4bf8f995e8b6f06db483cee2952de5662bb5cf37 Mon Sep 17 00:00:00 2001 | |||
| From: Ben Davis <ben.davis@arm.com> | |||
| Date: Mon, 1 Jun 2020 17:28:17 +0100 | |||
| Subject: [PATCH 16/18] drm: drm_fourcc: add NV15, Q410, Q401 YUV formats | |||
| DRM_FORMAT_NV15 is a 2 plane format suitable for linear and 16x16 | |||
| block-linear memory layouts (DRM_FORMAT_MOD_SAMSUNG_16_16_TILE). The | |||
| format is similar to P010 with 4:2:0 sub-sampling but has no padding | |||
| between components. Instead, luminance and chrominance samples are | |||
| grouped into 4s so that each group is packed into an integer number | |||
| of bytes: | |||
| YYYY = UVUV = 4 * 10 bits = 40 bits = 5 bytes | |||
| The '15' suffix refers to the optimum effective bits per pixel which is | |||
| achieved when the total number of luminance samples is a multiple of 8. | |||
| Q410 and Q401 are both 3 plane non-subsampled formats with 16 bits per | |||
| component, but only 10 bits are used and 6 are padded. 'Q' is chosen | |||
| as the first letter to denote 3 plane YUV444, (and is the next letter | |||
| along from P which is usually 2 plane). | |||
| V2: Updated block_w of NV15 to {4, 2, 0} | |||
| V3: Updated commit message to include specific modifier name | |||
| NV15: | |||
| Tested-by: Jonas Karlman <jonas@kwiboo.se> | |||
| Reviewed-by: Brian Starkey <brian.starkey@arm.com> | |||
| Signed-off-by: Ben Davis <ben.davis@arm.com> | |||
| Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com> | |||
| Link: https://patchwork.freedesktop.org/patch/msgid/20200601162817.18230-1-ben.davis@arm.com | |||
| --- | |||
| drivers/gpu/drm/drm_fourcc.c | 12 ++++++++++++ | |||
| include/uapi/drm/drm_fourcc.h | 22 ++++++++++++++++++++++ | |||
| 2 files changed, 34 insertions(+) | |||
| diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c | |||
| index b234bfaeda06..722c7ebe4e88 100644 | |||
| --- a/drivers/gpu/drm/drm_fourcc.c | |||
| +++ b/drivers/gpu/drm/drm_fourcc.c | |||
| @@ -274,6 +274,18 @@ const struct drm_format_info *__drm_format_info(u32 format) | |||
| { .format = DRM_FORMAT_YUV420_10BIT, .depth = 0, | |||
| .num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 2, .vsub = 2, | |||
| .is_yuv = true }, | |||
| + { .format = DRM_FORMAT_NV15, .depth = 0, | |||
| + .num_planes = 2, .char_per_block = { 5, 5, 0 }, | |||
| + .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2, | |||
| + .vsub = 2, .is_yuv = true }, | |||
| + { .format = DRM_FORMAT_Q410, .depth = 0, | |||
| + .num_planes = 3, .char_per_block = { 2, 2, 2 }, | |||
| + .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 0, | |||
| + .vsub = 0, .is_yuv = true }, | |||
| + { .format = DRM_FORMAT_Q401, .depth = 0, | |||
| + .num_planes = 3, .char_per_block = { 2, 2, 2 }, | |||
| + .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 0, | |||
| + .vsub = 0, .is_yuv = true }, | |||
| }; | |||
| unsigned int i; | |||
| diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h | |||
| index 490143500a50..8ba2d9153a94 100644 | |||
| --- a/include/uapi/drm/drm_fourcc.h | |||
| +++ b/include/uapi/drm/drm_fourcc.h | |||
| @@ -236,6 +236,12 @@ extern "C" { | |||
| #define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ | |||
| #define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */ | |||
| #define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */ | |||
| +/* | |||
| + * 2 plane YCbCr | |||
| + * index 0 = Y plane, [39:0] Y3:Y2:Y1:Y0 little endian | |||
| + * index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian | |||
| + */ | |||
| +#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */ | |||
| /* | |||
| * 2 plane YCbCr MSB aligned | |||
| @@ -265,6 +271,22 @@ extern "C" { | |||
| */ | |||
| #define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane 16 bits per channel */ | |||
| +/* 3 plane non-subsampled (444) YCbCr | |||
| + * 16 bits per component, but only 10 bits are used and 6 bits are padded | |||
| + * index 0: Y plane, [15:0] Y:x [10:6] little endian | |||
| + * index 1: Cb plane, [15:0] Cb:x [10:6] little endian | |||
| + * index 2: Cr plane, [15:0] Cr:x [10:6] little endian | |||
| + */ | |||
| +#define DRM_FORMAT_Q410 fourcc_code('Q', '4', '1', '0') | |||
| + | |||
| +/* 3 plane non-subsampled (444) YCrCb | |||
| + * 16 bits per component, but only 10 bits are used and 6 bits are padded | |||
| + * index 0: Y plane, [15:0] Y:x [10:6] little endian | |||
| + * index 1: Cr plane, [15:0] Cr:x [10:6] little endian | |||
| + * index 2: Cb plane, [15:0] Cb:x [10:6] little endian | |||
| + */ | |||
| +#define DRM_FORMAT_Q401 fourcc_code('Q', '4', '0', '1') | |||
| + | |||
| /* | |||
| * 3 plane YCbCr | |||
| * index 0: Y plane, [7:0] Y | |||
| -- | |||
| 2.25.1 | |||
| From 588b8545dda3cd0ef8f066dcafff3fa7a93e4056 Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 22:30:13 +0000 | |||
| Subject: [PATCH 17/18] drm: drm_fourcc: add NV20 and NV30 YUV formats | |||
| DRM_FORMAT_NV20 and DRM_FORMAT_NV30 formats is the 2x1 and non-subsampled | |||
| variant of NV15, a 10-bit 2-plane YUV format that has no padding between | |||
| components. Instead, luminance and chrominance samples are grouped into 4s | |||
| so that each group is packed into an integer number of bytes: | |||
| YYYY = UVUV = 4 * 10 bits = 40 bits = 5 bytes | |||
| The '20' and '30' suffix refers to the optimum effective bits per pixel | |||
| which is achieved when the total number of luminance samples is a multiple | |||
| of 4. | |||
| V2: Added NV30 format | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| Reviewed-by: Sandy Huang <hjc@rock-chips.com> | |||
| --- | |||
| drivers/gpu/drm/drm_fourcc.c | 8 ++++++++ | |||
| include/uapi/drm/drm_fourcc.h | 2 ++ | |||
| 2 files changed, 10 insertions(+) | |||
| diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c | |||
| index 722c7ebe4e88..2daf8a304b53 100644 | |||
| --- a/drivers/gpu/drm/drm_fourcc.c | |||
| +++ b/drivers/gpu/drm/drm_fourcc.c | |||
| @@ -278,6 +278,14 @@ const struct drm_format_info *__drm_format_info(u32 format) | |||
| .num_planes = 2, .char_per_block = { 5, 5, 0 }, | |||
| .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2, | |||
| .vsub = 2, .is_yuv = true }, | |||
| + { .format = DRM_FORMAT_NV20, .depth = 0, | |||
| + .num_planes = 2, .char_per_block = { 5, 5, 0 }, | |||
| + .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2, | |||
| + .vsub = 1, .is_yuv = true }, | |||
| + { .format = DRM_FORMAT_NV30, .depth = 0, | |||
| + .num_planes = 2, .char_per_block = { 5, 5, 0 }, | |||
| + .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 1, | |||
| + .vsub = 1, .is_yuv = true }, | |||
| { .format = DRM_FORMAT_Q410, .depth = 0, | |||
| .num_planes = 3, .char_per_block = { 2, 2, 2 }, | |||
| .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 0, | |||
| diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h | |||
| index 8ba2d9153a94..c3024c82a3e3 100644 | |||
| --- a/include/uapi/drm/drm_fourcc.h | |||
| +++ b/include/uapi/drm/drm_fourcc.h | |||
| @@ -242,6 +242,8 @@ extern "C" { | |||
| * index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian | |||
| */ | |||
| #define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */ | |||
| +#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */ | |||
| +#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */ | |||
| /* | |||
| * 2 plane YCbCr MSB aligned | |||
| -- | |||
| 2.25.1 | |||
| From 5e09593745d332f82471e1a763e9b9b2632543eb Mon Sep 17 00:00:00 2001 | |||
| From: Jonas Karlman <jonas@kwiboo.se> | |||
| Date: Mon, 6 Jul 2020 22:30:13 +0000 | |||
| Subject: [PATCH 18/18] drm: rockchip: add NV15, NV20 and NV30 support | |||
| Add support for displaying 10-bit 4:2:0 and 4:2:2 formats produced by the | |||
| Rockchip Video Decoder on RK322X, RK3288, RK3328, RK3368 and RK3399. | |||
| Also add support for 10-bit 4:4:4 format while at it. | |||
| V2: Added NV30 support | |||
| Signed-off-by: Jonas Karlman <jonas@kwiboo.se> | |||
| Reviewed-by: Sandy Huang <hjc@rock-chips.com> | |||
| --- | |||
| drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 29 +++++++++++++++++-- | |||
| drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 1 + | |||
| drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 32 +++++++++++++++++---- | |||
| 3 files changed, 54 insertions(+), 8 deletions(-) | |||
| diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | |||
| index 33463b79a37b..dc2667cfb575 100644 | |||
| --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | |||
| +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | |||
| @@ -261,6 +261,18 @@ static bool has_rb_swapped(uint32_t format) | |||
| } | |||
| } | |||
| +static bool is_fmt_10(uint32_t format) | |||
| +{ | |||
| + switch (format) { | |||
| + case DRM_FORMAT_NV15: | |||
| + case DRM_FORMAT_NV20: | |||
| + case DRM_FORMAT_NV30: | |||
| + return true; | |||
| + default: | |||
| + return false; | |||
| + } | |||
| +} | |||
| + | |||
| static enum vop_data_format vop_convert_format(uint32_t format) | |||
| { | |||
| switch (format) { | |||
| @@ -276,10 +288,13 @@ static enum vop_data_format vop_convert_format(uint32_t format) | |||
| case DRM_FORMAT_BGR565: | |||
| return VOP_FMT_RGB565; | |||
| case DRM_FORMAT_NV12: | |||
| + case DRM_FORMAT_NV15: | |||
| return VOP_FMT_YUV420SP; | |||
| case DRM_FORMAT_NV16: | |||
| + case DRM_FORMAT_NV20: | |||
| return VOP_FMT_YUV422SP; | |||
| case DRM_FORMAT_NV24: | |||
| + case DRM_FORMAT_NV30: | |||
| return VOP_FMT_YUV444SP; | |||
| default: | |||
| DRM_ERROR("unsupported format[%08x]\n", format); | |||
| @@ -922,7 +937,12 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
| dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start; | |||
| dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff); | |||
| - offset = (src->x1 >> 16) * fb->format->cpp[0]; | |||
| + if (fb->format->block_w[0]) | |||
| + offset = (src->x1 >> 16) * fb->format->char_per_block[0] / | |||
| + fb->format->block_w[0]; | |||
| + else | |||
| + offset = (src->x1 >> 16) * fb->format->cpp[0]; | |||
| + | |||
| offset += (src->y1 >> 16) * fb->pitches[0]; | |||
| dma_addr = rk_obj->dma_addr + offset + fb->offsets[0]; | |||
| @@ -948,6 +968,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
| } | |||
| VOP_WIN_SET(vop, win, format, format); | |||
| + VOP_WIN_SET(vop, win, fmt_10, is_fmt_10(fb->format->format)); | |||
| VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4)); | |||
| VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); | |||
| VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv); | |||
| @@ -964,7 +985,11 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
| uv_obj = fb->obj[1]; | |||
| rk_uv_obj = to_rockchip_obj(uv_obj); | |||
| - offset = (src->x1 >> 16) * bpp / hsub; | |||
| + if (fb->format->block_w[1]) | |||
| + offset = (src->x1 >> 16) * bpp / | |||
| + fb->format->block_w[1] / hsub; | |||
| + else | |||
| + offset = (src->x1 >> 16) * bpp / hsub; | |||
| offset += (src->y1 >> 16) * fb->pitches[1] / vsub; | |||
| dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1]; | |||
| diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h | |||
| index d03bdb531ef2..db1138da2bd4 100644 | |||
| --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h | |||
| +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h | |||
| @@ -154,6 +154,7 @@ struct vop_win_phy { | |||
| struct vop_reg enable; | |||
| struct vop_reg gate; | |||
| struct vop_reg format; | |||
| + struct vop_reg fmt_10; | |||
| struct vop_reg rb_swap; | |||
| struct vop_reg act_info; | |||
| struct vop_reg dsp_info; | |||
| diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c | |||
| index 2413deded22c..475c435d659b 100644 | |||
| --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c | |||
| +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c | |||
| @@ -50,6 +50,23 @@ static const uint32_t formats_win_full[] = { | |||
| DRM_FORMAT_NV24, | |||
| }; | |||
| +static const uint32_t formats_win_full_10[] = { | |||
| + DRM_FORMAT_XRGB8888, | |||
| + DRM_FORMAT_ARGB8888, | |||
| + DRM_FORMAT_XBGR8888, | |||
| + DRM_FORMAT_ABGR8888, | |||
| + DRM_FORMAT_RGB888, | |||
| + DRM_FORMAT_BGR888, | |||
| + DRM_FORMAT_RGB565, | |||
| + DRM_FORMAT_BGR565, | |||
| + DRM_FORMAT_NV12, | |||
| + DRM_FORMAT_NV16, | |||
| + DRM_FORMAT_NV24, | |||
| + DRM_FORMAT_NV15, | |||
| + DRM_FORMAT_NV20, | |||
| + DRM_FORMAT_NV30, | |||
| +}; | |||
| + | |||
| static const uint64_t format_modifiers_win_full[] = { | |||
| DRM_FORMAT_MOD_LINEAR, | |||
| DRM_FORMAT_MOD_INVALID, | |||
| @@ -570,11 +587,12 @@ static const struct vop_scl_regs rk3288_win_full_scl = { | |||
| static const struct vop_win_phy rk3288_win01_data = { | |||
| .scl = &rk3288_win_full_scl, | |||
| - .data_formats = formats_win_full, | |||
| - .nformats = ARRAY_SIZE(formats_win_full), | |||
| + .data_formats = formats_win_full_10, | |||
| + .nformats = ARRAY_SIZE(formats_win_full_10), | |||
| .format_modifiers = format_modifiers_win_full, | |||
| .enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0), | |||
| .format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1), | |||
| + .fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 4), | |||
| .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12), | |||
| .act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0), | |||
| .dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0), | |||
| @@ -704,11 +722,12 @@ static const struct vop_intr rk3368_vop_intr = { | |||
| static const struct vop_win_phy rk3368_win01_data = { | |||
| .scl = &rk3288_win_full_scl, | |||
| - .data_formats = formats_win_full, | |||
| - .nformats = ARRAY_SIZE(formats_win_full), | |||
| + .data_formats = formats_win_full_10, | |||
| + .nformats = ARRAY_SIZE(formats_win_full_10), | |||
| .format_modifiers = format_modifiers_win_full, | |||
| .enable = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 0), | |||
| .format = VOP_REG(RK3368_WIN0_CTRL0, 0x7, 1), | |||
| + .fmt_10 = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 4), | |||
| .rb_swap = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 12), | |||
| .x_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 21), | |||
| .y_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 22), | |||
| @@ -853,11 +872,12 @@ static const struct vop_win_yuv2yuv_data rk3399_vop_big_win_yuv2yuv_data[] = { | |||
| static const struct vop_win_phy rk3399_win01_data = { | |||
| .scl = &rk3288_win_full_scl, | |||
| - .data_formats = formats_win_full, | |||
| - .nformats = ARRAY_SIZE(formats_win_full), | |||
| + .data_formats = formats_win_full_10, | |||
| + .nformats = ARRAY_SIZE(formats_win_full_10), | |||
| .format_modifiers = format_modifiers_win_full_afbc, | |||
| .enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0), | |||
| .format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1), | |||
| + .fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 4), | |||
| .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12), | |||
| .y_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 22), | |||
| .act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0), | |||
| -- | |||
| 2.25.1 | |||
| @@ -0,0 +1,25 @@ | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| index 8a8cf0e..6cef1e3 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |||
| @@ -722,17 +722,17 @@ | |||
| trips { | |||
| cpu_alert0: cpu_alert0 { | |||
| - temperature = <70000>; | |||
| + temperature = <85000>; | |||
| hysteresis = <2000>; | |||
| type = "passive"; | |||
| }; | |||
| cpu_alert1: cpu_alert1 { | |||
| - temperature = <75000>; | |||
| + temperature = <95000>; | |||
| hysteresis = <2000>; | |||
| type = "passive"; | |||
| }; | |||
| cpu_crit: cpu_crit { | |||
| - temperature = <95000>; | |||
| + temperature = <100000>; | |||
| hysteresis = <2000>; | |||
| type = "critical"; | |||
| }; | |||
| @@ -0,0 +1,35 @@ | |||
| From e3ffe88f90644eddd0fa6ee8c9f324d54c3b7c39 Mon Sep 17 00:00:00 2001 | |||
| From: Igor Pecovnik <igor.pecovnik@gmail.com> | |||
| Date: Tue, 4 Aug 2020 19:35:47 +0200 | |||
| Subject: [PATCH] Add DT link for backward compatibility | |||
| Signed-off-by: Igor Pecovnik <igor.pecovnik@gmail.com> | |||
| --- | |||
| arch/arm64/boot/dts/rockchip/Makefile | 1 + | |||
| arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts | 1 + | |||
| 2 files changed, 2 insertions(+) | |||
| create mode 120000 arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts | |||
| diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile | |||
| index e91faee4a..8fb764404 100644 | |||
| --- a/arch/arm64/boot/dts/rockchip/Makefile | |||
| +++ b/arch/arm64/boot/dts/rockchip/Makefile | |||
| @@ -38,6 +38,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-pinebook-pro.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-puma-haikou.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-roc-pc.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-roc-pc-mezzanine.dtb | |||
| +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rock-pi-4.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rock-pi-4a.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rock-pi-4b.dtb | |||
| dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rock-pi-4c.dtb | |||
| diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts | |||
| new file mode 120000 | |||
| index 000000000..07e8a11b0 | |||
| --- /dev/null | |||
| +++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts | |||
| @@ -0,0 +1 @@ | |||
| +rk3399-rock-pi-4b.dts | |||
| \ No newline at end of file | |||
| -- | |||
| Created with Armbian build tools https://github.com/armbian/build | |||
| @@ -18438,6 +18438,7 @@ in | |||
| ubootClearfog | |||
| ubootGuruplug | |||
| ubootJetsonTK1 | |||
| ubootNanoPiM4v2 | |||
| ubootNovena | |||
| ubootOdroidC2 | |||
| ubootOdroidXU3 | |||