diff --git a/pkgs/misc/arm-trusted-firmware/atf-add-trust-ini.patch b/pkgs/misc/arm-trusted-firmware/atf-add-trust-ini.patch new file mode 100644 index 00000000000..528ec557087 --- /dev/null +++ b/pkgs/misc/arm-trusted-firmware/atf-add-trust-ini.patch @@ -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 diff --git a/pkgs/misc/arm-trusted-firmware/default.nix b/pkgs/misc/arm-trusted-firmware/default.nix index 596f5e90e34..b9ecfc3e2a9 100644 --- a/pkgs/misc/arm-trusted-firmware/default.nix +++ b/pkgs/misc/arm-trusted-firmware/default.nix @@ -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 diff --git a/pkgs/misc/arm-trusted-firmware/enable-logging.patch b/pkgs/misc/arm-trusted-firmware/enable-logging.patch new file mode 100644 index 00000000000..f327c1d3a24 --- /dev/null +++ b/pkgs/misc/arm-trusted-firmware/enable-logging.patch @@ -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"); diff --git a/pkgs/misc/uboot/add-board-nanopi-m4v2.patch b/pkgs/misc/uboot/add-board-nanopi-m4v2.patch new file mode 100644 index 00000000000..cd9b8da053f --- /dev/null +++ b/pkgs/misc/uboot/add-board-nanopi-m4v2.patch @@ -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 ++ */ ++ ++#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 diff --git a/pkgs/misc/uboot/default.nix b/pkgs/misc/uboot/default.nix index 66f41962666..16032c807fe 100644 --- a/pkgs/misc/uboot/default.nix +++ b/pkgs/misc/uboot/default.nix @@ -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"]; diff --git a/pkgs/misc/uboot/enable-DT-overlays-support.patch b/pkgs/misc/uboot/enable-DT-overlays-support.patch new file mode 100644 index 00000000000..3794a05c991 --- /dev/null +++ b/pkgs/misc/uboot/enable-DT-overlays-support.patch @@ -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" diff --git a/pkgs/misc/uboot/general-prioritize-sd.patch b/pkgs/misc/uboot/general-prioritize-sd.patch new file mode 100644 index 00000000000..b71d4a21194 --- /dev/null +++ b/pkgs/misc/uboot/general-prioritize-sd.patch @@ -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 diff --git a/pkgs/misc/uboot/rk3399-always-init-rkclk.patch b/pkgs/misc/uboot/rk3399-always-init-rkclk.patch new file mode 100644 index 00000000000..78e26a6418d --- /dev/null +++ b/pkgs/misc/uboot/rk3399-always-init-rkclk.patch @@ -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 + +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; + } + diff --git a/pkgs/misc/uboot/rk3399-enable-stable-mac.patch b/pkgs/misc/uboot/rk3399-enable-stable-mac.patch new file mode 100644 index 00000000000..5e2a9e431c2 --- /dev/null +++ b/pkgs/misc/uboot/rk3399-enable-stable-mac.patch @@ -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 diff --git a/pkgs/misc/uboot/rk3399-populate-child-node-of-syscon.patch b/pkgs/misc/uboot/rk3399-populate-child-node-of-syscon.patch new file mode 100644 index 00000000000..3f36d5919f2 --- /dev/null +++ b/pkgs/misc/uboot/rk3399-populate-child-node-of-syscon.patch @@ -0,0 +1,31 @@ +From 91cbd5f4fb25281319034c33b6e0c0f9b7d1e12b Mon Sep 17 00:00:00 2001 +Message-Id: <91cbd5f4fb25281319034c33b6e0c0f9b7d1e12b.1585676333.git.aditya@kobol.io> +In-Reply-To: +References: +From: Aditya Prayoga +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 + diff --git a/pkgs/misc/uboot/sdmmc-force-fifo-mode-in-spl.patch b/pkgs/misc/uboot/sdmmc-force-fifo-mode-in-spl.patch new file mode 100644 index 00000000000..9b7354f97ec --- /dev/null +++ b/pkgs/misc/uboot/sdmmc-force-fifo-mode-in-spl.patch @@ -0,0 +1,22 @@ +From b0693aeb9ceab57ffc9d9f4ceca610bd82d5ca07 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= +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 diff --git a/pkgs/misc/uboot/u-boot-add-trust-ini.patch b/pkgs/misc/uboot/u-boot-add-trust-ini.patch new file mode 100644 index 00000000000..793f34dd12f --- /dev/null +++ b/pkgs/misc/uboot/u-boot-add-trust-ini.patch @@ -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 diff --git a/pkgs/os-specific/linux/kernel/linux-5.8.nix b/pkgs/os-specific/linux/kernel/linux-5.8.nix index d9567b20e03..887120e715b 100644 --- a/pkgs/os-specific/linux/kernel/linux-5.8.nix +++ b/pkgs/os-specific/linux/kernel/linux-5.8.nix @@ -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 {})) diff --git a/pkgs/os-specific/linux/kernel/nanopi/0001-arm64-dts-rockchip-remove-capacity-dmips-rk3399.patch b/pkgs/os-specific/linux/kernel/nanopi/0001-arm64-dts-rockchip-remove-capacity-dmips-rk3399.patch new file mode 100644 index 00000000000..4898ecf3813 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/0001-arm64-dts-rockchip-remove-capacity-dmips-rk3399.patch @@ -0,0 +1,64 @@ +From 52505a426ae3a358708a2adc67d288b771df4192 Mon Sep 17 00:00:00 2001 +From: Dan Johansen +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 + diff --git a/pkgs/os-specific/linux/kernel/nanopi/0010-arm64-dts-rockchip-add-cw2015-node-to-PBP.patch b/pkgs/os-specific/linux/kernel/nanopi/0010-arm64-dts-rockchip-add-cw2015-node-to-PBP.patch new file mode 100644 index 00000000000..a0e3f0c1cee --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/0010-arm64-dts-rockchip-add-cw2015-node-to-PBP.patch @@ -0,0 +1,56 @@ +From bf967280def5ef5fe6e4cf1ce27196a9eb988cf5 Mon Sep 17 00:00:00 2001 +From: Dan Johansen +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 + diff --git a/pkgs/os-specific/linux/kernel/nanopi/0011-fix-wonky-wifi-bt-on-PBP.patch b/pkgs/os-specific/linux/kernel/nanopi/0011-fix-wonky-wifi-bt-on-PBP.patch new file mode 100644 index 00000000000..077454eb86e --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/0011-fix-wonky-wifi-bt-on-PBP.patch @@ -0,0 +1,150 @@ +From 0207f77ece3d07b964d5723c501adc3f3a5a3c6d Mon Sep 17 00:00:00 2001 +From: Dan Johansen +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 + diff --git a/pkgs/os-specific/linux/kernel/nanopi/0012-add-suspend-to-rk3399-PBP.patch b/pkgs/os-specific/linux/kernel/nanopi/0012-add-suspend-to-rk3399-PBP.patch new file mode 100644 index 00000000000..5f6c1f4dd54 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/0012-add-suspend-to-rk3399-PBP.patch @@ -0,0 +1,1033 @@ +From c7b16d749d1746e3ab081f8550c3aba252969d26 Mon Sep 17 00:00:00 2001 +From: Dan Johansen +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 + #include + #include ++#include + #include + + / { +@@ -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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++/* 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 + diff --git a/pkgs/os-specific/linux/kernel/nanopi/0013-arm64-dts-rockchip-setup-USB-type-c-port-as-dual-dat.patch b/pkgs/os-specific/linux/kernel/nanopi/0013-arm64-dts-rockchip-setup-USB-type-c-port-as-dual-dat.patch new file mode 100644 index 00000000000..03e2912af4f --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/0013-arm64-dts-rockchip-setup-USB-type-c-port-as-dual-dat.patch @@ -0,0 +1,26 @@ +From 7d27663e4c2b112fbd035ea1b4d3ed074e98fbec Mon Sep 17 00:00:00 2001 +From: Dan Johansen +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 + diff --git a/pkgs/os-specific/linux/kernel/nanopi/0015-add-dp-alt-mode-to-PBP.patch b/pkgs/os-specific/linux/kernel/nanopi/0015-add-dp-alt-mode-to-PBP.patch new file mode 100644 index 00000000000..096f9c596ed --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/0015-add-dp-alt-mode-to-PBP.patch @@ -0,0 +1,445 @@ +From a1bbffd1cbb883be7fe3da1d09c29d57cfbeb2da Mon Sep 17 00:00:00 2001 +From: Dan Johansen +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 { + ; + 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 + #include + #include ++#include + #include + #include + #include +@@ -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 ++#include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 + diff --git a/pkgs/os-specific/linux/kernel/nanopi/RK3399-enable_1.5_2.0_ghz_cpufreq_opp.patch b/pkgs/os-specific/linux/kernel/nanopi/RK3399-enable_1.5_2.0_ghz_cpufreq_opp.patch new file mode 100644 index 00000000000..ee91e343738 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/RK3399-enable_1.5_2.0_ghz_cpufreq_opp.patch @@ -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 { diff --git a/pkgs/os-specific/linux/kernel/nanopi/add-board-nanopi-m4v2.patch b/pkgs/os-specific/linux/kernel/nanopi/add-board-nanopi-m4v2.patch new file mode 100644 index 00000000000..ba844c8b800 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/add-board-nanopi-m4v2.patch @@ -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>; ++}; diff --git a/pkgs/os-specific/linux/kernel/nanopi/add-csgpio-to-rockchip-spi.patch b/pkgs/os-specific/linux/kernel/nanopi/add-csgpio-to-rockchip-spi.patch new file mode 100644 index 00000000000..b9278ddf003 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/add-csgpio-to-rockchip-spi.patch @@ -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 + #include ++#include + #include + #include + #include +@@ -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; diff --git a/pkgs/os-specific/linux/kernel/nanopi/add-fusb30x-driver.patch b/pkgs/os-specific/linux/kernel/nanopi/add-fusb30x-driver.patch new file mode 100644 index 00000000000..fa6ca6db3be --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/add-fusb30x-driver.patch @@ -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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 ++ * ++ * 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 ++#include ++ ++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 */ ++ diff --git a/pkgs/os-specific/linux/kernel/nanopi/add-maker-friendlyarm.patch b/pkgs/os-specific/linux/kernel/nanopi/add-maker-friendlyarm.patch new file mode 100644 index 00000000000..b8d5ff65fe9 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/add-maker-friendlyarm.patch @@ -0,0 +1,211 @@ +From 2fc2cbaaaf0dcebdeffa6e87bbc9ad843a5470dd Mon Sep 17 00:00:00 2001 +From: hmz007 +Date: Sat, 11 Jan 2020 19:35:03 +0800 +Subject: [PATCH] soc: friendlyelec: Add board info driver + +Change-Id: I122adb4f99c816b5c177f16392fb2df9c10a47be +Signed-off-by: hmz007 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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"); diff --git a/pkgs/os-specific/linux/kernel/nanopi/board-nanopi-m4v2-dts-add-sound-card.patch b/pkgs/os-specific/linux/kernel/nanopi/board-nanopi-m4v2-dts-add-sound-card.patch new file mode 100644 index 00000000000..6f5c6c62a69 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/board-nanopi-m4v2-dts-add-sound-card.patch @@ -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 diff --git a/pkgs/os-specific/linux/kernel/nanopi/board-nanopi-m4v2-dts-ethernet-tweak.patch b/pkgs/os-specific/linux/kernel/nanopi/board-nanopi-m4v2-dts-ethernet-tweak.patch new file mode 100644 index 00000000000..bb2cef90f27 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/board-nanopi-m4v2-dts-ethernet-tweak.patch @@ -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>; + }; diff --git a/pkgs/os-specific/linux/kernel/nanopi/fix-spi1-flash-speed.patch b/pkgs/os-specific/linux/kernel/nanopi/fix-spi1-flash-speed.patch new file mode 100644 index 00000000000..51dc2a9a8d0 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/fix-spi1-flash-speed.patch @@ -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>; + }; + }; + diff --git a/pkgs/os-specific/linux/kernel/nanopi/general-add-overlay-compilation-support.patch b/pkgs/os-specific/linux/kernel/nanopi/general-add-overlay-compilation-support.patch new file mode 100644 index 00000000000..a1ff52cef6a --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/general-add-overlay-compilation-support.patch @@ -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 diff --git a/pkgs/os-specific/linux/kernel/nanopi/general-add-overlay-configfs.patch b/pkgs/os-specific/linux/kernel/nanopi/general-add-overlay-configfs.patch new file mode 100644 index 00000000000..cc68eadccd7 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/general-add-overlay-configfs.patch @@ -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 +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 +Signed-off-by: Phil Elwell + +DT configfs: Fix build errors on other platforms + +Signed-off-by: Phil Elwell + +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 + +From 7cfb967c52d374ddcb0fc9194f38d1d5f9d8cdd8 Mon Sep 17 00:00:00 2001 +From: popcornmix +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 +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 + +From 274dfabb947ca32116a429c582c74aaee6ff1b5b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= +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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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); diff --git a/pkgs/os-specific/linux/kernel/nanopi/general-btsdio-ignore-uart-devs.patch b/pkgs/os-specific/linux/kernel/nanopi/general-btsdio-ignore-uart-devs.patch new file mode 100644 index 00000000000..8119bd8ad77 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/general-btsdio-ignore-uart-devs.patch @@ -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; + } diff --git a/pkgs/os-specific/linux/kernel/nanopi/general-emmc-hs400es-init-tweak.patch b/pkgs/os-specific/linux/kernel/nanopi/general-emmc-hs400es-init-tweak.patch new file mode 100644 index 00000000000..03144ffc310 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/general-emmc-hs400es-init-tweak.patch @@ -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, diff --git a/pkgs/os-specific/linux/kernel/nanopi/general-fix-es8316-kernel-panic.patch b/pkgs/os-specific/linux/kernel/nanopi/general-fix-es8316-kernel-panic.patch new file mode 100644 index 00000000000..3a27c0cb20c --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/general-fix-es8316-kernel-panic.patch @@ -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); + } diff --git a/pkgs/os-specific/linux/kernel/nanopi/general-increasing_DMA_block_memory_allocation_to_2048.patch b/pkgs/os-specific/linux/kernel/nanopi/general-increasing_DMA_block_memory_allocation_to_2048.patch new file mode 100644 index 00000000000..fc6c2485b82 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/general-increasing_DMA_block_memory_allocation_to_2048.patch @@ -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); + diff --git a/pkgs/os-specific/linux/kernel/nanopi/general-rockchip-overlays.patch b/pkgs/os-specific/linux/kernel/nanopi/general-rockchip-overlays.patch new file mode 100644 index 00000000000..c4752abc17b --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/general-rockchip-overlays.patch @@ -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)) diff --git a/pkgs/os-specific/linux/kernel/nanopi/general-rt5651-add-mclk.patch b/pkgs/os-specific/linux/kernel/nanopi/general-rt5651-add-mclk.patch new file mode 100644 index 00000000000..26c6320f802 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/general-rt5651-add-mclk.patch @@ -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 + #include + #include ++#include + + #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__ */ diff --git a/pkgs/os-specific/linux/kernel/nanopi/rk3399-add-sclk-i2sout-src-clock.patch b/pkgs/os-specific/linux/kernel/nanopi/rk3399-add-sclk-i2sout-src-clock.patch new file mode 100644 index 00000000000..5573ece228e --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/rk3399-add-sclk-i2sout-src-clock.patch @@ -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 diff --git a/pkgs/os-specific/linux/kernel/nanopi/rk3399-bump-voltages-for-low-opps.patch b/pkgs/os-specific/linux/kernel/nanopi/rk3399-bump-voltages-for-low-opps.patch new file mode 100644 index 00000000000..a7c9dee53dd --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/rk3399-bump-voltages-for-low-opps.patch @@ -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>; diff --git a/pkgs/os-specific/linux/kernel/nanopi/rk3399-sd-drive-level-8ma.patch b/pkgs/os-specific/linux/kernel/nanopi/rk3399-sd-drive-level-8ma.patch new file mode 100644 index 00000000000..cac129aa047 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/rk3399-sd-drive-level-8ma.patch @@ -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>; + }; + }; + diff --git a/pkgs/os-specific/linux/kernel/nanopi/rkvdec_nv15.patch b/pkgs/os-specific/linux/kernel/nanopi/rkvdec_nv15.patch new file mode 100644 index 00000000000..ac531c4fd7d --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/rkvdec_nv15.patch @@ -0,0 +1,1602 @@ +From bbfdef43b6f4a65b960ebe6e540233a4c26e9374 Mon Sep 17 00:00:00 2001 +From: Ezequiel Garcia +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 +--- + 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 +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 +--- + 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 +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 +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 +Reviewed-by: Ezequiel Garcia +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +Reviewed-by: Ezequiel Garcia +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + .../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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +Reviewed-by: Ezequiel Garcia +--- + 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 +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 + +Reviewed-by: Brian Starkey +Signed-off-by: Ben Davis +Signed-off-by: Liviu Dudau +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 +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 +Reviewed-by: Sandy Huang +--- + 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 +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 +Reviewed-by: Sandy Huang +--- + 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 + diff --git a/pkgs/os-specific/linux/kernel/nanopi/unlock-temperature.patch b/pkgs/os-specific/linux/kernel/nanopi/unlock-temperature.patch new file mode 100644 index 00000000000..c1c2196041b --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/unlock-temperature.patch @@ -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"; + }; diff --git a/pkgs/os-specific/linux/kernel/nanopi/zzz-0004-Add-DT-link-for-backward-compatibility.patch b/pkgs/os-specific/linux/kernel/nanopi/zzz-0004-Add-DT-link-for-backward-compatibility.patch new file mode 100644 index 00000000000..f27663efab1 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/nanopi/zzz-0004-Add-DT-link-for-backward-compatibility.patch @@ -0,0 +1,35 @@ +From e3ffe88f90644eddd0fa6ee8c9f324d54c3b7c39 Mon Sep 17 00:00:00 2001 +From: Igor Pecovnik +Date: Tue, 4 Aug 2020 19:35:47 +0200 +Subject: [PATCH] Add DT link for backward compatibility + +Signed-off-by: Igor Pecovnik +--- + 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 + diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 43d1856ad4b..0d70751a942 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -18438,6 +18438,7 @@ in ubootClearfog ubootGuruplug ubootJetsonTK1 + ubootNanoPiM4v2 ubootNovena ubootOdroidC2 ubootOdroidXU3