Browse Source

wip

wip-nanopi-m4v2
Mitch Tishmack 6 years ago
parent
commit
73b9448a94
42 changed files with 9767 additions and 6 deletions
  1. +21
    -0
      pkgs/misc/arm-trusted-firmware/atf-add-trust-ini.patch
  2. +7
    -2
      pkgs/misc/arm-trusted-firmware/default.nix
  3. +31
    -0
      pkgs/misc/arm-trusted-firmware/enable-logging.patch
  4. +189
    -0
      pkgs/misc/uboot/add-board-nanopi-m4v2.patch
  5. +28
    -4
      pkgs/misc/uboot/default.nix
  6. +13
    -0
      pkgs/misc/uboot/enable-DT-overlays-support.patch
  7. +15
    -0
      pkgs/misc/uboot/general-prioritize-sd.patch
  8. +54
    -0
      pkgs/misc/uboot/rk3399-always-init-rkclk.patch
  9. +15
    -0
      pkgs/misc/uboot/rk3399-enable-stable-mac.patch
  10. +31
    -0
      pkgs/misc/uboot/rk3399-populate-child-node-of-syscon.patch
  11. +22
    -0
      pkgs/misc/uboot/sdmmc-force-fifo-mode-in-spl.patch
  12. +21
    -0
      pkgs/misc/uboot/u-boot-add-trust-ini.patch
  13. +49
    -0
      pkgs/os-specific/linux/kernel/linux-5.8.nix
  14. +64
    -0
      pkgs/os-specific/linux/kernel/nanopi/0001-arm64-dts-rockchip-remove-capacity-dmips-rk3399.patch
  15. +56
    -0
      pkgs/os-specific/linux/kernel/nanopi/0010-arm64-dts-rockchip-add-cw2015-node-to-PBP.patch
  16. +150
    -0
      pkgs/os-specific/linux/kernel/nanopi/0011-fix-wonky-wifi-bt-on-PBP.patch
  17. +1033
    -0
      pkgs/os-specific/linux/kernel/nanopi/0012-add-suspend-to-rk3399-PBP.patch
  18. +26
    -0
      pkgs/os-specific/linux/kernel/nanopi/0013-arm64-dts-rockchip-setup-USB-type-c-port-as-dual-dat.patch
  19. +445
    -0
      pkgs/os-specific/linux/kernel/nanopi/0015-add-dp-alt-mode-to-PBP.patch
  20. +26
    -0
      pkgs/os-specific/linux/kernel/nanopi/RK3399-enable_1.5_2.0_ghz_cpufreq_opp.patch
  21. +84
    -0
      pkgs/os-specific/linux/kernel/nanopi/add-board-nanopi-m4v2.patch
  22. +83
    -0
      pkgs/os-specific/linux/kernel/nanopi/add-csgpio-to-rockchip-spi.patch
  23. +4047
    -0
      pkgs/os-specific/linux/kernel/nanopi/add-fusb30x-driver.patch
  24. +211
    -0
      pkgs/os-specific/linux/kernel/nanopi/add-maker-friendlyarm.patch
  25. +106
    -0
      pkgs/os-specific/linux/kernel/nanopi/board-nanopi-m4v2-dts-add-sound-card.patch
  26. +15
    -0
      pkgs/os-specific/linux/kernel/nanopi/board-nanopi-m4v2-dts-ethernet-tweak.patch
  27. +13
    -0
      pkgs/os-specific/linux/kernel/nanopi/fix-spi1-flash-speed.patch
  28. +82
    -0
      pkgs/os-specific/linux/kernel/nanopi/general-add-overlay-compilation-support.patch
  29. +430
    -0
      pkgs/os-specific/linux/kernel/nanopi/general-add-overlay-configfs.patch
  30. +19
    -0
      pkgs/os-specific/linux/kernel/nanopi/general-btsdio-ignore-uart-devs.patch
  31. +22
    -0
      pkgs/os-specific/linux/kernel/nanopi/general-emmc-hs400es-init-tweak.patch
  32. +11
    -0
      pkgs/os-specific/linux/kernel/nanopi/general-fix-es8316-kernel-panic.patch
  33. +21
    -0
      pkgs/os-specific/linux/kernel/nanopi/general-increasing_DMA_block_memory_allocation_to_2048.patch
  34. +490
    -0
      pkgs/os-specific/linux/kernel/nanopi/general-rockchip-overlays.patch
  35. +60
    -0
      pkgs/os-specific/linux/kernel/nanopi/general-rt5651-add-mclk.patch
  36. +25
    -0
      pkgs/os-specific/linux/kernel/nanopi/rk3399-add-sclk-i2sout-src-clock.patch
  37. +40
    -0
      pkgs/os-specific/linux/kernel/nanopi/rk3399-bump-voltages-for-low-opps.patch
  38. +49
    -0
      pkgs/os-specific/linux/kernel/nanopi/rk3399-sd-drive-level-8ma.patch
  39. +1602
    -0
      pkgs/os-specific/linux/kernel/nanopi/rkvdec_nv15.patch
  40. +25
    -0
      pkgs/os-specific/linux/kernel/nanopi/unlock-temperature.patch
  41. +35
    -0
      pkgs/os-specific/linux/kernel/nanopi/zzz-0004-Add-DT-link-for-backward-compatibility.patch
  42. +1
    -0
      pkgs/top-level/all-packages.nix

+ 21
- 0
pkgs/misc/arm-trusted-firmware/atf-add-trust-ini.patch View File

@@ -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

+ 7
- 2
pkgs/misc/arm-trusted-firmware/default.nix View File

@@ -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


+ 31
- 0
pkgs/misc/arm-trusted-firmware/enable-logging.patch View File

@@ -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");

+ 189
- 0
pkgs/misc/uboot/add-board-nanopi-m4v2.patch View File

@@ -0,0 +1,189 @@
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 9900b442..afa9ec81 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -128,6 +128,7 @@ dtb-$(CONFIG_ROCKCHIP_RK3399) += \
rk3399-nanopc-t4.dtb \
rk3399-nanopi-m4.dtb \
rk3399-nanopi-m4-2gb.dtb \
+ rk3399-nanopi-m4v2.dtb \
rk3399-nanopi-neo4.dtb \
rk3399-orangepi.dtb \
rk3399-pinebook-pro.dtb \
diff --git a/arch/arm/dts/rk3399-nanopi-m4v2-u-boot.dtsi b/arch/arm/dts/rk3399-nanopi-m4v2-u-boot.dtsi
new file mode 100644
index 00000000..4b775f6d
--- /dev/null
+++ b/arch/arm/dts/rk3399-nanopi-m4v2-u-boot.dtsi
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Jagan Teki <jagan@amarulasolutions.com>
+ */
+
+#include "rk3399-nanopi4-u-boot.dtsi"
+#include "rk3399-sdram-lpddr4-100.dtsi"
+
+/ {
+ chosen {
+ u-boot,spl-boot-order = &sdmmc, &sdhci;
+ };
+};
+
+&sdio0 {
+ status = "disabled";
+};
diff --git a/arch/arm/dts/rk3399-nanopi-m4v2.dts b/arch/arm/dts/rk3399-nanopi-m4v2.dts
new file mode 100644
index 00000000..eac77f2a
--- /dev/null
+++ b/arch/arm/dts/rk3399-nanopi-m4v2.dts
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * FriendlyElec NanoPi M4 board device tree source
+ *
+ * Copyright (c) 2018 FriendlyElec Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ *
+ * Copyright (c) 2018 Collabora Ltd.
+ * Copyright (c) 2019 Arm Ltd.
+ */
+
+/dts-v1/;
+#include "rk3399-nanopi4.dtsi"
+
+/ {
+ model = "FriendlyElec NanoPi M4V2";
+ compatible = "friendlyarm,nanopi-m4", "rockchip,rk3399";
+
+ vdd_5v: vdd-5v {
+ compatible = "regulator-fixed";
+ regulator-name = "vdd_5v";
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ vcc5v0_core: vcc5v0-core {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_core";
+ regulator-always-on;
+ regulator-boot-on;
+ vin-supply = <&vdd_5v>;
+ };
+
+ vcc5v0_usb1: vcc5v0-usb1 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_usb1";
+ regulator-always-on;
+ regulator-boot-on;
+ vin-supply = <&vcc5v0_sys>;
+ };
+
+ vcc5v0_usb2: vcc5v0-usb2 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_usb2";
+ regulator-always-on;
+ regulator-boot-on;
+ vin-supply = <&vcc5v0_sys>;
+ };
+};
+
+&vcc3v3_sys {
+ vin-supply = <&vcc5v0_core>;
+};
+
+&u2phy0_host {
+ phy-supply = <&vcc5v0_usb1>;
+};
+
+&u2phy1_host {
+ phy-supply = <&vcc5v0_usb2>;
+};
+
+&vbus_typec {
+ regulator-always-on;
+ vin-supply = <&vdd_5v>;
+};
diff --git a/configs/nanopi-m4v2-rk3399_defconfig b/configs/nanopi-m4v2-rk3399_defconfig
new file mode 100644
index 00000000..dd26f73d
--- /dev/null
+++ b/configs/nanopi-m4v2-rk3399_defconfig
@@ -0,0 +1,76 @@
+CONFIG_ARM=y
+CONFIG_ARCH_ROCKCHIP=y
+CONFIG_SYS_TEXT_BASE=0x00200000
+CONFIG_ENV_OFFSET=0x3F8000
+CONFIG_ROCKCHIP_RK3399=y
+CONFIG_TARGET_EVB_RK3399=y
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_DEBUG_UART_BASE=0xFF1A0000
+CONFIG_DEBUG_UART_CLOCK=24000000
+CONFIG_DEBUG_UART=y
+CONFIG_DEFAULT_FDT_FILE="rockchip/rk3399-nanopi-m4v2.dtb"
+CONFIG_MISC_INIT_R=y
+# CONFIG_DISPLAY_CPUINFO is not set
+CONFIG_DISPLAY_BOARDINFO_LATE=y
+# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set
+CONFIG_SPL_STACK_R=y
+CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x10000
+CONFIG_TPL=y
+CONFIG_CMD_BOOTZ=y
+CONFIG_CMD_GPT=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_USB=y
+CONFIG_CMD_PCI=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_TIME=y
+CONFIG_SPL_OF_CONTROL=y
+CONFIG_DEFAULT_DEVICE_TREE="rk3399-nanopi-m4v2"
+CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
+CONFIG_ENV_IS_IN_MMC=y
+CONFIG_SYS_RELOC_GD_ENV_ADDR=y
+CONFIG_ROCKCHIP_GPIO=y
+CONFIG_SYS_I2C_ROCKCHIP=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_ROCKCHIP=y
+CONFIG_DM_ETH=y
+CONFIG_ETH_DESIGNWARE=y
+CONFIG_GMAC_ROCKCHIP=y
+CONFIG_NVME=y
+CONFIG_PCI=y
+CONFIG_PMIC_RK8XX=y
+CONFIG_REGULATOR_PWM=y
+CONFIG_REGULATOR_RK8XX=y
+CONFIG_PWM_ROCKCHIP=y
+CONFIG_RAM_RK3399_LPDDR4=y
+CONFIG_DM_RESET=y
+CONFIG_BAUDRATE=1500000
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_SYSRESET=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_XHCI_DWC3=n
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_GENERIC=y
+CONFIG_USB_HOST_ETHER=y
+CONFIG_USB_ETHER_ASIX=y
+CONFIG_USB_ETHER_ASIX88179=y
+CONFIG_USB_ETHER_MCS7830=y
+CONFIG_USB_ETHER_RTL8152=y
+CONFIG_USB_ETHER_SMSC95XX=y
+CONFIG_USB_KEYBOARD=y
+CONFIG_SPL_TINY_MEMSET=y
+CONFIG_ERRNO_STR=y
+CONFIG_DM_VIDEO=y
+CONFIG_VIDEO_BPP16=y
+CONFIG_VIDEO_BPP32=y
+CONFIG_DISPLAY=y
+CONFIG_VIDEO_ROCKCHIP=y
+CONFIG_DISPLAY_ROCKCHIP_HDMI=y
+CONFIG_LOG=y
+CONFIG_LOG_CONSOLE=y
+CONFIG_SPL_LOG=y
+CONFIG_SPL_LOG_CONSOLE=y
+CONFIG_LOG_MAX_LEVEL=7
+# CONFIG_USE_TINY_PRINTF is not set

+ 28
- 4
pkgs/misc/uboot/default.nix View File

@@ -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"];


+ 13
- 0
pkgs/misc/uboot/enable-DT-overlays-support.patch View File

@@ -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"

+ 15
- 0
pkgs/misc/uboot/general-prioritize-sd.patch View File

@@ -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

+ 54
- 0
pkgs/misc/uboot/rk3399-always-init-rkclk.patch View File

@@ -0,0 +1,54 @@
This patch enables clk initialisation of rk3399 cpu in u-boot proper.

Normally it should only be initialised in SPL as it is "time consuming".
Doing so however leaves cpus clocked to low frequencies
for Rockchip's DDR/loader/trust with mainline u-boot scenario
which does not involve SPL phase.

Signed-off-by: Piotr Szczepanik <piter75@gmail.com>

diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
index 865b80cc..fe546436 100644
--- a/drivers/clk/rockchip/clk_rk3399.c
+++ b/drivers/clk/rockchip/clk_rk3399.c
@@ -47,12 +47,9 @@ struct pll_div {
.fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\
.postdiv1 = _postdiv1, .postdiv2 = _postdiv2};
-#if defined(CONFIG_SPL_BUILD)
static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1);
static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2, 2);
-#else
static const struct pll_div ppll_init_cfg = PLL_DIVISORS(PPLL_HZ, 2, 2, 1);
-#endif
static const struct pll_div apll_l_1600_cfg = PLL_DIVISORS(1600 * MHz, 3, 1, 1);
static const struct pll_div apll_l_600_cfg = PLL_DIVISORS(600 * MHz, 1, 2, 1);
@@ -1071,7 +1068,6 @@ static struct clk_ops rk3399_clk_ops = {
#endif
};
-#ifdef CONFIG_SPL_BUILD
static void rkclk_init(struct rockchip_cru *cru)
{
u32 aclk_div;
@@ -1149,11 +1145,9 @@ static void rkclk_init(struct rockchip_cru *cru)
hclk_div << HCLK_PERILP1_DIV_CON_SHIFT |
HCLK_PERILP1_PLL_SEL_GPLL << HCLK_PERILP1_PLL_SEL_SHIFT);
}
-#endif
static int rk3399_clk_probe(struct udevice *dev)
{
-#ifdef CONFIG_SPL_BUILD
struct rk3399_clk_priv *priv = dev_get_priv(dev);
#if CONFIG_IS_ENABLED(OF_PLATDATA)
@@ -1162,7 +1156,6 @@ static int rk3399_clk_probe(struct udevice *dev)
priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]);
#endif
rkclk_init(priv->cru);
-#endif
return 0;
}

+ 15
- 0
pkgs/misc/uboot/rk3399-enable-stable-mac.patch View File

@@ -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

+ 31
- 0
pkgs/misc/uboot/rk3399-populate-child-node-of-syscon.patch View File

@@ -0,0 +1,31 @@
From 91cbd5f4fb25281319034c33b6e0c0f9b7d1e12b Mon Sep 17 00:00:00 2001
Message-Id: <91cbd5f4fb25281319034c33b6e0c0f9b7d1e12b.1585676333.git.aditya@kobol.io>
In-Reply-To: <ef8793354dbb6bce3027a3492ffc1fee3588f41f.1585676333.git.aditya@kobol.io>
References: <ef8793354dbb6bce3027a3492ffc1fee3588f41f.1585676333.git.aditya@kobol.io>
From: Aditya Prayoga <aditya@kobol.io>
Date: Wed, 4 Mar 2020 22:10:31 +0700
Subject: [PATCH 4/4] arm:rockchip:rk3399: Populate child node of syscon

U-Boot only populate first level of node.
Scan child node so device such as PHY can be initialized.
---
arch/arm/mach-rockchip/rk3399/syscon_rk3399.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c b/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c
index 259ca44d68..81b04aa7f8 100644
--- a/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c
+++ b/arch/arm/mach-rockchip/rk3399/syscon_rk3399.c
@@ -21,6 +21,9 @@ U_BOOT_DRIVER(syscon_rk3399) = {
.name = "rk3399_syscon",
.id = UCLASS_SYSCON,
.of_match = rk3399_syscon_ids,
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+ .bind = dm_scan_fdt_dev,
+#endif
};
#if CONFIG_IS_ENABLED(OF_PLATDATA)
--
2.17.1


+ 22
- 0
pkgs/misc/uboot/sdmmc-force-fifo-mode-in-spl.patch View File

@@ -0,0 +1,22 @@
From b0693aeb9ceab57ffc9d9f4ceca610bd82d5ca07 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= <ayufan@ayufan.eu>
Date: Mon, 21 May 2018 02:00:19 +0200
Subject: [PATCH] ayufan: rock64: for SPL build always use fifo-mode

Change-Id: I9ac012ce4aaf03a151f7c5c818829d631efdd7ed
diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c
index fc0f0fad76..d1f26e41fe 100644
--- a/drivers/mmc/rockchip_dw_mmc.c
+++ b/drivers/mmc/rockchip_dw_mmc.c
@@ -70,7 +70,11 @@ static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev)
if (priv->fifo_depth < 0)
return -EINVAL;
+#ifdef CONFIG_SPL_BUILD
+ priv->fifo_mode = true; // always force fifo mode
+#else
priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
+#endif
/*
* 'clock-freq-min-max' is deprecated

+ 21
- 0
pkgs/misc/uboot/u-boot-add-trust-ini.patch View File

@@ -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

+ 49
- 0
pkgs/os-specific/linux/kernel/linux-5.8.nix View File

@@ -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 {}))

+ 64
- 0
pkgs/os-specific/linux/kernel/nanopi/0001-arm64-dts-rockchip-remove-capacity-dmips-rk3399.patch View File

@@ -0,0 +1,64 @@
From 52505a426ae3a358708a2adc67d288b771df4192 Mon Sep 17 00:00:00 2001
From: Dan Johansen <strit@manjaro.org>
Date: Mon, 16 Sep 2019 19:46:48 +0200
Subject: [PATCH 1/3] arm64-dts-rockchip-remove-capacity-dmips-rk3399

---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 6 ------
1 file changed, 6 deletions(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index cede1ad81be2..e6e224205573 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -71,7 +71,6 @@
compatible = "arm,cortex-a53";
reg = <0x0 0x0>;
enable-method = "psci";
- capacity-dmips-mhz = <485>;
clocks = <&cru ARMCLKL>;
#cooling-cells = <2>; /* min followed by max */
dynamic-power-coefficient = <100>;
@@ -83,7 +82,6 @@
compatible = "arm,cortex-a53";
reg = <0x0 0x1>;
enable-method = "psci";
- capacity-dmips-mhz = <485>;
clocks = <&cru ARMCLKL>;
#cooling-cells = <2>; /* min followed by max */
dynamic-power-coefficient = <100>;
@@ -95,7 +93,6 @@
compatible = "arm,cortex-a53";
reg = <0x0 0x2>;
enable-method = "psci";
- capacity-dmips-mhz = <485>;
clocks = <&cru ARMCLKL>;
#cooling-cells = <2>; /* min followed by max */
dynamic-power-coefficient = <100>;
@@ -107,7 +104,6 @@
compatible = "arm,cortex-a53";
reg = <0x0 0x3>;
enable-method = "psci";
- capacity-dmips-mhz = <485>;
clocks = <&cru ARMCLKL>;
#cooling-cells = <2>; /* min followed by max */
dynamic-power-coefficient = <100>;
@@ -119,7 +115,6 @@
compatible = "arm,cortex-a72";
reg = <0x0 0x100>;
enable-method = "psci";
- capacity-dmips-mhz = <1024>;
clocks = <&cru ARMCLKB>;
#cooling-cells = <2>; /* min followed by max */
dynamic-power-coefficient = <436>;
@@ -131,7 +126,6 @@
compatible = "arm,cortex-a72";
reg = <0x0 0x101>;
enable-method = "psci";
- capacity-dmips-mhz = <1024>;
clocks = <&cru ARMCLKB>;
#cooling-cells = <2>; /* min followed by max */
dynamic-power-coefficient = <436>;
--
2.23.0


+ 56
- 0
pkgs/os-specific/linux/kernel/nanopi/0010-arm64-dts-rockchip-add-cw2015-node-to-PBP.patch View File

@@ -0,0 +1,56 @@
From bf967280def5ef5fe6e4cf1ce27196a9eb988cf5 Mon Sep 17 00:00:00 2001
From: Dan Johansen <strit@manjaro.org>
Date: Sat, 30 May 2020 17:19:41 +0200
Subject: [PATCH] arm64-dts-rockchip-add-cw2015-node-to-PBP

---
.../boot/dts/rockchip/rk3399-pinebook-pro.dts | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
index c49982dfd8fc..e814917c013b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
@@ -28,6 +28,13 @@ backlight: edp-backlight {
pwms = <&pwm0 0 740740 0>;
};
+ bat: battery {
+ compatible = "simple-battery";
+ charge-full-design-microamp-hours = <9800000>;
+ voltage-max-design-microvolt = <4350000>;
+ voltage-min-design-microvolt = <3000000>;
+ };
+
edp_panel: edp-panel {
compatible = "boe,nv140fhmn49";
backlight = <&backlight>;
@@ -741,6 +748,25 @@ usbc_dp: endpoint {
};
};
};
+
+ cw2015@62 {
+ compatible = "cellwise,cw2015";
+ reg = <0x62>;
+ cellwise,battery-profile = /bits/ 8 <
+ 0x17 0x67 0x80 0x73 0x6E 0x6C 0x6B 0x63
+ 0x77 0x51 0x5C 0x58 0x50 0x4C 0x48 0x36
+ 0x15 0x0C 0x0C 0x19 0x5B 0x7D 0x6F 0x69
+ 0x69 0x5B 0x0C 0x29 0x20 0x40 0x52 0x59
+ 0x57 0x56 0x54 0x4F 0x3B 0x1F 0x7F 0x17
+ 0x06 0x1A 0x30 0x5A 0x85 0x93 0x96 0x2D
+ 0x48 0x77 0x9C 0xB3 0x80 0x52 0x94 0xCB
+ 0x2F 0x00 0x64 0xA5 0xB5 0x11 0xF0 0x11
+ >;
+ cellwise,monitor-interval-ms = <5000>;
+ monitored-battery = <&bat>;
+ power-supplies = <&mains_charger>, <&fusb0>;
+ };
+
};
&i2s1 {
--
2.26.2


+ 150
- 0
pkgs/os-specific/linux/kernel/nanopi/0011-fix-wonky-wifi-bt-on-PBP.patch View File

@@ -0,0 +1,150 @@
From 0207f77ece3d07b964d5723c501adc3f3a5a3c6d Mon Sep 17 00:00:00 2001
From: Dan Johansen <strit@manjaro.org>
Date: Mon, 1 Jun 2020 17:14:50 +0200
Subject: [PATCH] fix wonky wifi/bt on PBP

---
drivers/bluetooth/hci_bcm.c | 17 +++++++++++++++++
drivers/bluetooth/hci_serdev.c | 2 ++
drivers/mmc/core/pwrseq_simple.c | 19 ++++++++++++++++---
drivers/tty/serdev/core.c | 11 +++++++++++
include/linux/serdev.h | 1 +
5 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index b236cb11c0dc..bfd37fb9eeb0 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -1472,6 +1472,22 @@ static void bcm_serdev_remove(struct serdev_device *serdev)
hci_uart_unregister_device(&bcmdev->serdev_hu);
}
+static void bcm_serdev_shutdown(struct serdev_device *serdev)
+{
+ struct bcm_device *bcmdev = serdev_device_get_drvdata(serdev);
+
+/*
+ if (test_bit(HCI_UART_REGISTERED, &bcmdev->hu->flags)) {
+ hci_uart_unregister_device(&bcmdev->serdev_hu);
+ }
+*/
+ dev_info(bcmdev->dev, "Cutting power to bluetooth module\n");
+ if (bcm_gpio_set_power(bcmdev, false)) {
+ dev_err(bcmdev->dev, "Failed to power down\n");
+ }
+ usleep_range(500000, 1000000);
+}
+
#ifdef CONFIG_OF
static struct bcm_device_data bcm4354_device_data = {
.no_early_set_baudrate = true,
@@ -1497,6 +1513,7 @@ MODULE_DEVICE_TABLE(of, bcm_bluetooth_of_match);
static struct serdev_device_driver bcm_serdev_driver = {
.probe = bcm_serdev_probe,
.remove = bcm_serdev_remove,
+ .shutdown = bcm_serdev_shutdown,
.driver = {
.name = "hci_uart_bcm",
.of_match_table = of_match_ptr(bcm_bluetooth_of_match),
diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
index 4652896d4990..043c585b34a7 100644
--- a/drivers/bluetooth/hci_serdev.c
+++ b/drivers/bluetooth/hci_serdev.c
@@ -364,5 +364,7 @@ void hci_uart_unregister_device(struct hci_uart *hu)
hu->proto->close(hu);
serdev_device_close(hu->serdev);
+
+clear_bit(HCI_UART_REGISTERED, &hu->flags);
}
EXPORT_SYMBOL_GPL(hci_uart_unregister_device);
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index ea4d3670560e..b52c3f5b4f13 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -80,10 +80,8 @@ static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
msleep(pwrseq->post_power_on_delay_ms);
}
-static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
+static void __mmc_pwrseq_simple_power_off(struct mmc_pwrseq_simple *pwrseq)
{
- struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
-
mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
if (pwrseq->power_off_delay_us)
@@ -96,6 +94,12 @@ static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
}
}
+static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
+{
+ struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
+ __mmc_pwrseq_simple_power_off(pwrseq);
+}
+
static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
.pre_power_on = mmc_pwrseq_simple_pre_power_on,
.post_power_on = mmc_pwrseq_simple_post_power_on,
@@ -151,9 +155,18 @@ static int mmc_pwrseq_simple_remove(struct platform_device *pdev)
return 0;
}
+static void mmc_pwrseq_simple_shutdown(struct platform_device *pdev)
+{
+ struct mmc_pwrseq_simple *pwrseq = platform_get_drvdata(pdev);
+
+ dev_info(&pdev->dev, "Turning off mmc\n");
+ __mmc_pwrseq_simple_power_off(pwrseq);
+}
+
static struct platform_driver mmc_pwrseq_simple_driver = {
.probe = mmc_pwrseq_simple_probe,
.remove = mmc_pwrseq_simple_remove,
+ .shutdown = mmc_pwrseq_simple_shutdown,
.driver = {
.name = "pwrseq_simple",
.of_match_table = mmc_pwrseq_simple_of_match,
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index c5f0d936b003..54bcb38f0c05 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -432,11 +432,22 @@ static int serdev_drv_remove(struct device *dev)
return 0;
}
+static void serdev_drv_shutdown(struct device *dev)
+{
+ const struct serdev_device_driver *sdrv;
+ if (dev->driver) {
+ sdrv = to_serdev_device_driver(dev->driver);
+ if (sdrv->shutdown)
+ sdrv->shutdown(to_serdev_device(dev));
+ }
+}
+
static struct bus_type serdev_bus_type = {
.name = "serial",
.match = serdev_device_match,
.probe = serdev_drv_probe,
.remove = serdev_drv_remove,
+ .shutdown = serdev_drv_shutdown,
};
/**
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index 9f14f9c12ec4..c3d5dccd6115 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -63,6 +63,7 @@ struct serdev_device_driver {
struct device_driver driver;
int (*probe)(struct serdev_device *);
void (*remove)(struct serdev_device *);
+ void (*shutdown)(struct serdev_device *);
};
static inline struct serdev_device_driver *to_serdev_device_driver(struct device_driver *d)
--
2.26.2


+ 1033
- 0
pkgs/os-specific/linux/kernel/nanopi/0012-add-suspend-to-rk3399-PBP.patch View File

@@ -0,0 +1,1033 @@
From c7b16d749d1746e3ab081f8550c3aba252969d26 Mon Sep 17 00:00:00 2001
From: Dan Johansen <strit@manjaro.org>
Date: Mon, 1 Jun 2020 23:14:16 +0200
Subject: [PATCH] add suspend to rk3399/PBP

---
.../soc/rockchip/rockchip-pm-config.txt | 40 +++
.../boot/dts/rockchip/rk3399-pinebook-pro.dts | 35 +++
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 24 ++
drivers/firmware/Kconfig | 7 +
drivers/firmware/Makefile | 1 +
drivers/firmware/rockchip_sip.c | 263 ++++++++++++++++++
drivers/regulator/core.c | 49 +++-
drivers/soc/rockchip/Kconfig | 6 +
drivers/soc/rockchip/Makefile | 1 +
drivers/soc/rockchip/rockchip_pm_config.c | 213 ++++++++++++++
include/dt-bindings/suspend/rockchip-rk3399.h | 60 ++++
include/linux/regulator/driver.h | 3 +
include/linux/rockchip/rockchip_sip.h | 149 ++++++++++
13 files changed, 847 insertions(+), 4 deletions(-)
create mode 100644 Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt
create mode 100644 drivers/firmware/rockchip_sip.c
create mode 100644 drivers/soc/rockchip/rockchip_pm_config.c
create mode 100644 include/dt-bindings/suspend/rockchip-rk3399.h
create mode 100644 include/linux/rockchip/rockchip_sip.h

diff --git a/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt b/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt
new file mode 100644
index 000000000000..e15af33d3a9f
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt
@@ -0,0 +1,40 @@
+* the suspend mode config
+
+- compatible: "rockchip,pm-config"
+ Compatibility with rk3399
+
+- rockchip,sleep-mode-config : the sleep mode config,
+ ARMOFF, OSC disabled ...
+
+- rockchip,wakeup-config: the wake up sourece enable.
+ GPIO, USB, SD...
+
+- rockchip,pwm-regulator-config: the pwm regulator name.
+
+Example:
+ rockchip_suspend: rockchip_suspend {
+ compatible = "rockchip,pm-rk3399";
+ status = "okay";
+ rockchip,sleep-mode-config = <
+ (0
+ | RKPM_SLP_ARMPD
+ | RKPM_SLP_PERILPPD
+ | RKPM_SLP_DDR_RET
+ | RKPM_SLP_PLLPD
+ | RKPM_SLP_OSC_DIS
+ | RKPM_SLP_CENTER_PD
+ | RKPM_SLP_AP_PWROFF
+ )
+ >;
+ rockchip,wakeup-config = <
+ (0 |
+ RKPM_GPIO_WKUP_EN |
+ RKPM_PWM_WKUP_EN)
+ >;
+ rockchip,pwm-regulator-config = <
+ (0 |
+ PWM2_REGULATOR_EN
+ )
+ >;
+ };
+
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
index c49982dfd8fc..9643cae1537a 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
@@ -19,9 +19,15 @@ / {
compatible = "pine64,pinebook-pro", "rockchip,rk3399";
chosen {
+ bootargs = "earlycon=uart8250,mmio32,0xff1a0000";
stdout-path = "serial2:1500000n8";
};
+ memory {
+ device_type = "memory";
+ reg = <0x0 0x00200000 0x0 0xf7e00000>;
+ };
+
backlight: edp-backlight {
compatible = "pwm-backlight";
power-supply = <&vcc_12v>;
@@ -125,6 +131,12 @@ sdio_pwrseq: sdio-pwrseq {
reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>;
};
+ /* first 128k(0xff8d0000~0xff8f0000) for ddr and ATF */
+ sram@ff8d0000 {
+ compatible = "mmio-sram";
+ reg = <0x0 0xff8d0000 0x0 0x20000>; /* 128k */
+ };
+
/* Audio components */
es8316-sound {
compatible = "simple-audio-card";
@@ -906,6 +918,29 @@ &pwm2 {
status = "okay";
};
+&rockchip_suspend {
+ status = "okay";
+ rockchip,sleep-debug-en = <1>;
+ rockchip,sleep-mode-config = <
+ (0
+ | RKPM_SLP_ARMPD
+ | RKPM_SLP_PERILPPD
+ | RKPM_SLP_DDR_RET
+ | RKPM_SLP_PLLPD
+ | RKPM_SLP_CENTER_PD
+ | RKPM_SLP_AP_PWROFF
+ )
+ >;
+ rockchip,pwm-regulator-config = <
+ (0
+ | PWM2_REGULATOR_EN
+ )
+ >;
+ rockchip,power-ctrl =
+ <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>,
+ <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>;
+};
+
&saradc {
vref-supply = <&vcca1v8_s3>;
status = "okay";
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 1448f358ed0a..a91fc5c8b43b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -9,6 +9,7 @@
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/power/rk3399-power.h>
+#include <dt-bindings/suspend/rockchip-rk3399.h>
#include <dt-bindings/thermal/thermal.h>
/ {
@@ -2652,4 +2653,27 @@ pcie_clkreqnb_cpm: pci-clkreqnb-cpm {
};
};
+
+ rockchip_suspend: rockchip-suspend {
+ compatible = "rockchip,pm-rk3399";
+ status = "disabled";
+ rockchip,sleep-debug-en = <0>;
+ rockchip,virtual-poweroff = <0>;
+ rockchip,sleep-mode-config = <
+ (0
+ | RKPM_SLP_ARMPD
+ | RKPM_SLP_PERILPPD
+ | RKPM_SLP_DDR_RET
+ | RKPM_SLP_PLLPD
+ | RKPM_SLP_OSC_DIS
+ | RKPM_SLP_CENTER_PD
+ | RKPM_SLP_AP_PWROFF
+ )
+ >;
+ rockchip,wakeup-config = <
+ (0
+ | RKPM_GPIO_WKUP_EN
+ )
+ >;
+ };
};
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 8007d4aa76dc..37f8039a5e27 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -295,6 +295,13 @@ config TURRIS_MOX_RWTM
other manufacturing data and also utilize the Entropy Bit Generator
for hardware random number generation.
+config ROCKCHIP_SIP
+ bool "Rockchip SIP interface"
+ depends on ARM64 && ARM_PSCI_FW
+ help
+ Say Y here if you want to enable SIP callbacks for Rockchip platforms
+ This option enables support for communicating with the ATF.
+
source "drivers/firmware/broadcom/Kconfig"
source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index e9fb838af4df..575f45d55939 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -29,6 +29,7 @@ obj-y += meson/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
obj-$(CONFIG_EFI) += efi/
obj-$(CONFIG_UEFI_CPER) += efi/
+obj-$(CONFIG_ROCKCHIP_SIP) += rockchip_sip.o
obj-y += imx/
obj-y += tegra/
obj-y += xilinx/
diff --git a/drivers/firmware/rockchip_sip.c b/drivers/firmware/rockchip_sip.c
new file mode 100644
index 000000000000..6844c5bad5b1
--- /dev/null
+++ b/drivers/firmware/rockchip_sip.c
@@ -0,0 +1,263 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/io.h>
+#include <linux/rockchip/rockchip_sip.h>
+#include <asm/cputype.h>
+#include <asm/smp_plat.h>
+#include <uapi/linux/psci.h>
+#include <linux/ptrace.h>
+
+#ifdef CONFIG_64BIT
+#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name
+#else
+#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name
+#endif
+
+#define SIZE_PAGE(n) ((n) << 12)
+
+static struct arm_smccc_res __invoke_sip_fn_smc(unsigned long function_id,
+ unsigned long arg0,
+ unsigned long arg1,
+ unsigned long arg2)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
+ return res;
+}
+
+struct arm_smccc_res sip_smc_ddr_cfg(u32 arg0, u32 arg1, u32 arg2)
+{
+ return __invoke_sip_fn_smc(SIP_DDR_CFG, arg0, arg1, arg2);
+}
+
+struct arm_smccc_res sip_smc_get_atf_version(void)
+{
+ return __invoke_sip_fn_smc(SIP_ATF_VERSION, 0, 0, 0);
+}
+
+struct arm_smccc_res sip_smc_get_sip_version(void)
+{
+ return __invoke_sip_fn_smc(SIP_SIP_VERSION, 0, 0, 0);
+}
+
+int sip_smc_set_suspend_mode(u32 ctrl, u32 config1, u32 config2)
+{
+ struct arm_smccc_res res;
+
+ res = __invoke_sip_fn_smc(SIP_SUSPEND_MODE, ctrl, config1, config2);
+ return res.a0;
+}
+
+int sip_smc_virtual_poweroff(void)
+{
+ struct arm_smccc_res res;
+
+ res = __invoke_sip_fn_smc(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), 0, 0, 0);
+ return res.a0;
+}
+
+struct arm_smccc_res sip_smc_request_share_mem(u32 page_num,
+ share_page_type_t page_type)
+{
+ struct arm_smccc_res res;
+ unsigned long share_mem_phy;
+
+ res = __invoke_sip_fn_smc(SIP_SHARE_MEM, page_num, page_type, 0);
+ if (IS_SIP_ERROR(res.a0))
+ goto error;
+
+ share_mem_phy = res.a1;
+ res.a1 = (unsigned long)ioremap(share_mem_phy, SIZE_PAGE(page_num));
+
+error:
+ return res;
+}
+
+struct arm_smccc_res sip_smc_mcu_el3fiq(u32 arg0, u32 arg1, u32 arg2)
+{
+ return __invoke_sip_fn_smc(SIP_MCU_EL3FIQ_CFG, arg0, arg1, arg2);
+}
+
+/************************** fiq debugger **************************************/
+#ifdef CONFIG_ARM64
+#define SIP_UARTDBG_FN SIP_UARTDBG_CFG64
+#else
+#define SIP_UARTDBG_FN SIP_UARTDBG_CFG
+#endif
+
+static int fiq_sip_enabled;
+static int fiq_target_cpu;
+static phys_addr_t ft_fiq_mem_phy;
+static void __iomem *ft_fiq_mem_base;
+static void (*sip_fiq_debugger_uart_irq_tf)(struct pt_regs _pt_regs,
+ unsigned long cpu);
+int sip_fiq_debugger_is_enabled(void)
+{
+ return fiq_sip_enabled;
+}
+
+static struct pt_regs sip_fiq_debugger_get_pt_regs(void *reg_base,
+ unsigned long sp_el1)
+{
+ struct pt_regs fiq_pt_regs;
+
+#ifdef CONFIG_ARM64
+ /* copy cpu context */
+ memcpy(&fiq_pt_regs, reg_base, 8 * 31);
+
+ /* copy pstate */
+ memcpy(&fiq_pt_regs.pstate, reg_base + 0x110, 8);
+
+ /* EL1 mode */
+ if (fiq_pt_regs.pstate & 0x10)
+ memcpy(&fiq_pt_regs.sp, reg_base + 0xf8, 8);
+ /* EL0 mode */
+ else
+ fiq_pt_regs.sp = sp_el1;
+
+ /* copy pc */
+ memcpy(&fiq_pt_regs.pc, reg_base + 0x118, 8);
+#else
+ struct sm_nsec_ctx *nsec_ctx = reg_base;
+
+ fiq_pt_regs.ARM_r0 = nsec_ctx->r0;
+ fiq_pt_regs.ARM_r1 = nsec_ctx->r1;
+ fiq_pt_regs.ARM_r2 = nsec_ctx->r2;
+ fiq_pt_regs.ARM_r3 = nsec_ctx->r3;
+ fiq_pt_regs.ARM_r4 = nsec_ctx->r4;
+ fiq_pt_regs.ARM_r5 = nsec_ctx->r5;
+ fiq_pt_regs.ARM_r6 = nsec_ctx->r6;
+ fiq_pt_regs.ARM_r7 = nsec_ctx->r7;
+ fiq_pt_regs.ARM_r8 = nsec_ctx->r8;
+ fiq_pt_regs.ARM_r9 = nsec_ctx->r9;
+ fiq_pt_regs.ARM_r10 = nsec_ctx->r10;
+ fiq_pt_regs.ARM_fp = nsec_ctx->r11;
+ fiq_pt_regs.ARM_ip = nsec_ctx->r12;
+ fiq_pt_regs.ARM_sp = nsec_ctx->svc_sp;
+ fiq_pt_regs.ARM_lr = nsec_ctx->svc_lr;
+ fiq_pt_regs.ARM_pc = nsec_ctx->mon_lr;
+ fiq_pt_regs.ARM_cpsr = nsec_ctx->mon_spsr;
+#endif
+
+ return fiq_pt_regs;
+}
+
+static void sip_fiq_debugger_uart_irq_tf_cb(unsigned long sp_el1,
+ unsigned long offset,
+ unsigned long cpu)
+{
+ struct pt_regs fiq_pt_regs;
+ char *cpu_context;
+
+ /* calling fiq handler */
+ if (ft_fiq_mem_base) {
+ cpu_context = (char *)ft_fiq_mem_base + offset;
+ fiq_pt_regs = sip_fiq_debugger_get_pt_regs(cpu_context, sp_el1);
+ sip_fiq_debugger_uart_irq_tf(fiq_pt_regs, cpu);
+ }
+
+ /* fiq handler done, return to EL3(then EL3 return to EL1 entry) */
+ __invoke_sip_fn_smc(SIP_UARTDBG_FN, 0, 0, UARTDBG_CFG_OSHDL_TO_OS);
+}
+
+int sip_fiq_debugger_uart_irq_tf_init(u32 irq_id, void *callback_fn)
+{
+ struct arm_smccc_res res;
+
+ fiq_target_cpu = 0;
+
+ /* init fiq debugger callback */
+ sip_fiq_debugger_uart_irq_tf = callback_fn;
+ res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, irq_id,
+ (unsigned long)sip_fiq_debugger_uart_irq_tf_cb,
+ UARTDBG_CFG_INIT);
+ if (IS_SIP_ERROR(res.a0)) {
+ pr_err("%s error: %d\n", __func__, (int)res.a0);
+ return res.a0;
+ }
+
+ /* share memory ioremap */
+ if (!ft_fiq_mem_base) {
+ ft_fiq_mem_phy = res.a1;
+ ft_fiq_mem_base = ioremap(ft_fiq_mem_phy,
+ FIQ_UARTDBG_SHARE_MEM_SIZE);
+ if (!ft_fiq_mem_base) {
+ pr_err("%s: share memory ioremap failed\n", __func__);
+ return -ENOMEM;
+ }
+ }
+
+ fiq_sip_enabled = 1;
+
+ return SIP_RET_SUCCESS;
+}
+
+int sip_fiq_debugger_switch_cpu(u32 cpu)
+{
+ struct arm_smccc_res res;
+
+ fiq_target_cpu = cpu;
+ res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, cpu_logical_map(cpu),
+ 0, UARTDBG_CFG_OSHDL_CPUSW);
+ return res.a0;
+}
+
+void sip_fiq_debugger_enable_debug(bool enable)
+{
+ unsigned long val;
+
+ val = enable ? UARTDBG_CFG_OSHDL_DEBUG_ENABLE :
+ UARTDBG_CFG_OSHDL_DEBUG_DISABLE;
+
+ __invoke_sip_fn_smc(SIP_UARTDBG_FN, 0, 0, val);
+}
+
+int sip_fiq_debugger_set_print_port(u32 port_phyaddr, u32 baudrate)
+{
+ struct arm_smccc_res res;
+
+ res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, port_phyaddr, baudrate,
+ UARTDBG_CFG_PRINT_PORT);
+ return res.a0;
+}
+
+int sip_fiq_debugger_request_share_memory(void)
+{
+ struct arm_smccc_res res;
+
+ /* request page share memory */
+ res = sip_smc_request_share_mem(FIQ_UARTDBG_PAGE_NUMS,
+ SHARE_PAGE_TYPE_UARTDBG);
+ if (IS_SIP_ERROR(res.a0))
+ return res.a0;
+
+ return SIP_RET_SUCCESS;
+}
+
+int sip_fiq_debugger_get_target_cpu(void)
+{
+ return fiq_target_cpu;
+}
+
+void sip_fiq_debugger_enable_fiq(bool enable, uint32_t tgt_cpu)
+{
+ u32 en;
+
+ fiq_target_cpu = tgt_cpu;
+ en = enable ? UARTDBG_CFG_FIQ_ENABEL : UARTDBG_CFG_FIQ_DISABEL;
+ __invoke_sip_fn_smc(SIP_UARTDBG_FN, tgt_cpu, 0, en);
+}
+
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 7486f6e4e613..d46a267ea309 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -5268,6 +5268,14 @@ void regulator_unregister(struct regulator_dev *rdev)
EXPORT_SYMBOL_GPL(regulator_unregister);
#ifdef CONFIG_SUSPEND
+static inline int can_enable(struct regulator_dev *rdev) {
+ return rdev->ena_pin || rdev->desc->ops->enable;
+}
+
+static inline int can_disable(struct regulator_dev *rdev) {
+ return rdev->ena_pin || rdev->desc->ops->disable;
+}
+
/**
* regulator_suspend - prepare regulators for system wide suspend
* @dev: ``&struct device`` pointer that is passed to _regulator_suspend()
@@ -5278,10 +5286,34 @@ static int regulator_suspend(struct device *dev)
{
struct regulator_dev *rdev = dev_to_rdev(dev);
suspend_state_t state = pm_suspend_target_state;
+ struct regulator_state *rstate;
int ret;
regulator_lock(rdev);
ret = suspend_set_state(rdev, state);
+ if (ret) {
+ goto out;
+ }
+
+ rstate = regulator_get_suspend_state(rdev, state);
+ if (rstate == NULL)
+ goto out;
+
+ if (rstate->enabled == ENABLE_IN_SUSPEND && can_enable(rdev)) {
+ if (!rdev->desc->ops->set_suspend_enable) {
+ rdev->resume_state = _regulator_is_enabled(rdev);
+ rdev_info(rdev, "Entering suspend %d, enabling forcibly, was %s\n", state, rdev->resume_state ? "on" : "off");
+ ret = _regulator_do_enable(rdev);
+ }
+ } else if (rstate->enabled == DISABLE_IN_SUSPEND && can_disable(rdev)) {
+ if (!rdev->desc->ops->set_suspend_disable) {
+ rdev->resume_state = _regulator_is_enabled(rdev);
+ rdev_info(rdev, "Entering suspend %d, disabling forcibly, was %s\n", state, rdev->resume_state ? "on" : "off");
+ ret = _regulator_do_disable(rdev);
+ }
+ }
+out:
+
regulator_unlock(rdev);
return ret;
@@ -5300,10 +5332,19 @@ static int regulator_resume(struct device *dev)
regulator_lock(rdev);
- if (rdev->desc->ops->resume &&
- (rstate->enabled == ENABLE_IN_SUSPEND ||
- rstate->enabled == DISABLE_IN_SUSPEND))
- ret = rdev->desc->ops->resume(rdev);
+ if (rstate->enabled == ENABLE_IN_SUSPEND || rstate->enabled == DISABLE_IN_SUSPEND) {
+ if (rdev->desc->ops->resume) {
+ ret = rdev->desc->ops->resume(rdev);
+ } else if ((rstate->enabled == ENABLE_IN_SUSPEND && !rdev->desc->ops->set_suspend_enable) ||
+ (rstate->enabled == DISABLE_IN_SUSPEND && !rdev->desc->ops->set_suspend_disable)) {
+ rdev_info(rdev, "Resuming, restoring state to %s\n", rdev->resume_state ? "on" : "off");
+ if (rdev->resume_state && can_enable(rdev)) {
+ ret = _regulator_do_enable(rdev);
+ } else if (!rdev->resume_state && can_disable(rdev)) {
+ ret = _regulator_do_disable(rdev);
+ }
+ }
+ }
regulator_unlock(rdev);
diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig
index b71b73bf5fc5..bfadbecd0df8 100644
--- a/drivers/soc/rockchip/Kconfig
+++ b/drivers/soc/rockchip/Kconfig
@@ -26,4 +26,10 @@ config ROCKCHIP_PM_DOMAINS
If unsure, say N.
+config ROCKCHIP_SUSPEND_MODE
+ bool "Rockchip suspend mode config"
+ depends on ROCKCHIP_SIP
+ help
+ Say Y here if you want to set the suspend mode to the ATF.
+
endif
diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile
index afca0a4c4b72..a15c0a395a33 100644
--- a/drivers/soc/rockchip/Makefile
+++ b/drivers/soc/rockchip/Makefile
@@ -4,3 +4,4 @@
#
obj-$(CONFIG_ROCKCHIP_GRF) += grf.o
obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm_domains.o
+obj-$(CONFIG_ROCKCHIP_SUSPEND_MODE) += rockchip_pm_config.o
diff --git a/drivers/soc/rockchip/rockchip_pm_config.c b/drivers/soc/rockchip/rockchip_pm_config.c
new file mode 100644
index 000000000000..4a3038f22639
--- /dev/null
+++ b/drivers/soc/rockchip/rockchip_pm_config.c
@@ -0,0 +1,213 @@
+/*
+ * Rockchip Generic power configuration support.
+ *
+ * Copyright (c) 2017 ROCKCHIP, Co. Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/bitops.h>
+#include <linux/cpu.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/machine.h>
+#include <linux/rockchip/rockchip_sip.h>
+#include <linux/suspend.h>
+#include <dt-bindings/input/input.h>
+
+#define PM_INVALID_GPIO 0xffff
+
+static const struct of_device_id pm_match_table[] = {
+ { .compatible = "rockchip,pm-rk3399",},
+ { },
+};
+
+#define MAX_PWRKEY_NUMS 20
+#define MAX_NUM_KEYS 60
+
+struct rkxx_remote_key_table {
+ int scancode;
+ int keycode;
+};
+
+static int parse_ir_pwrkeys(unsigned int *pwrkey, int size, int *nkey)
+{
+ struct device_node *node;
+ struct device_node *child_node;
+ struct rkxx_remote_key_table key_table[MAX_NUM_KEYS];
+ int i;
+ int len = 0, nbuttons;
+ int num = 0;
+ u32 usercode, scancode;
+
+ for_each_node_by_name(node, "pwm") {
+ for_each_child_of_node(node, child_node) {
+ if (of_property_read_u32(child_node,
+ "rockchip,usercode",
+ &usercode))
+ break;
+
+ if (of_get_property(child_node,
+ "rockchip,key_table",
+ &len) == NULL ||
+ len <= 0)
+ break;
+
+ len = len < sizeof(key_table) ? len : sizeof(key_table);
+ len /= sizeof(u32);
+ if (of_property_read_u32_array(child_node,
+ "rockchip,key_table",
+ (u32 *)key_table,
+ len))
+ break;
+
+ nbuttons = len / 2;
+ for (i = 0; i < nbuttons && num < size; ++i) {
+ if (key_table[i].keycode == KEY_POWER) {
+ scancode = key_table[i].scancode;
+ pr_debug("usercode=%x, key=%x\n",
+ usercode, scancode);
+ pwrkey[num] = (usercode & 0xffff) << 16;
+ pwrkey[num] |= (scancode & 0xff) << 8;
+ ++num;
+ }
+ }
+ }
+ }
+
+ *nkey = num;
+
+ return num ? 0 : -1;
+}
+
+static void rockchip_pm_virt_pwroff_prepare(void)
+{
+ int error;
+ int i, nkey;
+ u32 power_key[MAX_PWRKEY_NUMS];
+
+ if ((parse_ir_pwrkeys(power_key, ARRAY_SIZE(power_key), &nkey))) {
+ pr_err("Parse ir powerkey code failed!\n");
+ return;
+ }
+
+ for (i = 0; i < nkey; ++i)
+ sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 1, power_key[i]);
+
+ regulator_suspend_prepare(PM_SUSPEND_MEM);
+
+ error = disable_nonboot_cpus();
+ if (error) {
+ pr_err("Disable nonboot cpus failed!\n");
+ return;
+ }
+
+ sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 0, 1);
+ sip_smc_virtual_poweroff();
+}
+
+static int __init pm_config_init(struct platform_device *pdev)
+{
+ const struct of_device_id *match_id;
+ struct device_node *node;
+ u32 mode_config = 0;
+ u32 wakeup_config = 0;
+ u32 pwm_regulator_config = 0;
+ int gpio_temp[10];
+ u32 sleep_debug_en = 0;
+ u32 apios_suspend = 0;
+ u32 virtual_poweroff_en = 0;
+ enum of_gpio_flags flags;
+ int i = 0;
+ int length;
+
+ match_id = of_match_node(pm_match_table, pdev->dev.of_node);
+ if (!match_id)
+ return -ENODEV;
+
+ node = of_find_node_by_name(NULL, "rockchip-suspend");
+
+ if (IS_ERR_OR_NULL(node)) {
+ dev_err(&pdev->dev, "%s dev node err\n", __func__);
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32_array(node,
+ "rockchip,sleep-mode-config",
+ &mode_config, 1))
+ dev_warn(&pdev->dev, "not set sleep mode config\n");
+ else
+ sip_smc_set_suspend_mode(SUSPEND_MODE_CONFIG, mode_config, 0);
+
+ if (of_property_read_u32_array(node,
+ "rockchip,wakeup-config",
+ &wakeup_config, 1))
+ dev_warn(&pdev->dev, "not set wakeup-config\n");
+ else
+ sip_smc_set_suspend_mode(WKUP_SOURCE_CONFIG, wakeup_config, 0);
+
+ if (of_property_read_u32_array(node,
+ "rockchip,pwm-regulator-config",
+ &pwm_regulator_config, 1))
+ dev_warn(&pdev->dev, "not set pwm-regulator-config\n");
+ else
+ sip_smc_set_suspend_mode(PWM_REGULATOR_CONFIG,
+ pwm_regulator_config,
+ 0);
+
+ length = of_gpio_named_count(node, "rockchip,power-ctrl");
+
+ if (length > 0 && length < 10) {
+ for (i = 0; i < length; i++) {
+ gpio_temp[i] = of_get_named_gpio_flags(node,
+ "rockchip,power-ctrl",
+ i,
+ &flags);
+ if (!gpio_is_valid(gpio_temp[i]))
+ break;
+ sip_smc_set_suspend_mode(GPIO_POWER_CONFIG,
+ i,
+ gpio_temp[i]);
+ }
+ }
+ sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, i, PM_INVALID_GPIO);
+
+ if (!of_property_read_u32_array(node,
+ "rockchip,sleep-debug-en",
+ &sleep_debug_en, 1))
+ sip_smc_set_suspend_mode(SUSPEND_DEBUG_ENABLE,
+ sleep_debug_en,
+ 0);
+
+ if (!of_property_read_u32_array(node,
+ "rockchip,apios-suspend",
+ &apios_suspend, 1))
+ sip_smc_set_suspend_mode(APIOS_SUSPEND_CONFIG,
+ apios_suspend,
+ 0);
+
+ if (!of_property_read_u32_array(node,
+ "rockchip,virtual-poweroff",
+ &virtual_poweroff_en, 1) &&
+ virtual_poweroff_en)
+ pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare;
+
+ return 0;
+}
+
+static struct platform_driver pm_driver = {
+ .driver = {
+ .name = "rockchip-pm",
+ .of_match_table = pm_match_table,
+ },
+};
+
+static int __init rockchip_pm_drv_register(void)
+{
+ return platform_driver_probe(&pm_driver, pm_config_init);
+}
+subsys_initcall(rockchip_pm_drv_register);
+
diff --git a/include/dt-bindings/suspend/rockchip-rk3399.h b/include/dt-bindings/suspend/rockchip-rk3399.h
new file mode 100644
index 000000000000..de23670b43ea
--- /dev/null
+++ b/include/dt-bindings/suspend/rockchip-rk3399.h
@@ -0,0 +1,60 @@
+/*
+ * Header providing constants for Rockchip suspend bindings.
+ *
+ * Copyright (C) 2017, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Tony.Xie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DT_BINDINGS_SUSPEND_ROCKCHIP_RK3399_H__
+#define __DT_BINDINGS_SUSPEND_ROCKCHIP_RK3399_H__
+
+/* the suspend mode */
+#define RKPM_SLP_WFI (1 << 0)
+#define RKPM_SLP_ARMPD (1 << 1)
+#define RKPM_SLP_PERILPPD (1 << 2)
+#define RKPM_SLP_DDR_RET (1 << 3)
+#define RKPM_SLP_PLLPD (1 << 4)
+#define RKPM_SLP_OSC_DIS (1 << 5)
+#define RKPM_SLP_CENTER_PD (1 << 6)
+#define RKPM_SLP_AP_PWROFF (1 << 7)
+
+/* the wake up source */
+#define RKPM_CLUSTER_L_WKUP_EN (1 << 0)
+#define RKPM_CLUSTER_B_WKUPB_EN (1 << 1)
+#define RKPM_GPIO_WKUP_EN (1 << 2)
+#define RKPM_SDIO_WKUP_EN (1 << 3)
+#define RKPM_SDMMC_WKUP_EN (1 << 4)
+#define RKPM_TIMER_WKUP_EN (1 << 6)
+#define RKPM_USB_WKUP_EN (1 << 7)
+#define RKPM_SFT_WKUP_EN (1 << 8)
+#define RKPM_WDT_M0_WKUP_EN (1 << 9)
+#define RKPM_TIME_OUT_WKUP_EN (1 << 10)
+#define RKPM_PWM_WKUP_EN (1 << 11)
+#define RKPM_PCIE_WKUP_EN (1 << 13)
+
+/* the pwm regulator */
+#define PWM0_REGULATOR_EN (1 << 0)
+#define PWM1_REGULATOR_EN (1 << 1)
+#define PWM2_REGULATOR_EN (1 << 2)
+#define PWM3A_REGULATOR_EN (1 << 3)
+#define PWM3B_REGULATOR_EN (1 << 4)
+
+/* the APIO voltage domain */
+#define RKPM_APIO0_SUSPEND (1 << 0)
+#define RKPM_APIO1_SUSPEND (1 << 1)
+#define RKPM_APIO2_SUSPEND (1 << 2)
+#define RKPM_APIO3_SUSPEND (1 << 3)
+#define RKPM_APIO4_SUSPEND (1 << 4)
+#define RKPM_APIO5_SUSPEND (1 << 5)
+
+#endif
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 29d920516e0b..549b68c58e68 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -482,6 +482,9 @@ struct regulator_dev {
/* time when this regulator was disabled last time */
unsigned long last_off_jiffy;
+
+ /* state when resuming */
+ int resume_state;
};
struct regulator_dev *
diff --git a/include/linux/rockchip/rockchip_sip.h b/include/linux/rockchip/rockchip_sip.h
new file mode 100644
index 000000000000..9e8bbb2251c6
--- /dev/null
+++ b/include/linux/rockchip/rockchip_sip.h
@@ -0,0 +1,149 @@
+/* Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __ROCKCHIP_SIP_H
+#define __ROCKCHIP_SIP_H
+
+#include <linux/arm-smccc.h>
+#include <linux/io.h>
+
+/* SMC function IDs for SiP Service queries, compatible with kernel-3.10 */
+#define SIP_ATF_VERSION 0x82000001
+#define SIP_ACCESS_REG 0x82000002
+#define SIP_SUSPEND_MODE 0x82000003
+#define SIP_PENDING_CPUS 0x82000004
+#define SIP_UARTDBG_CFG 0x82000005
+#define SIP_UARTDBG_CFG64 0xc2000005
+#define SIP_MCU_EL3FIQ_CFG 0x82000006
+#define SIP_ACCESS_CHIP_STATE64 0xc2000006
+#define SIP_SECURE_MEM_CONFIG 0x82000007
+#define SIP_ACCESS_CHIP_EXTRA_STATE64 0xc2000007
+#define SIP_DDR_CFG 0x82000008
+#define SIP_SHARE_MEM 0x82000009
+#define SIP_SIP_VERSION 0x8200000a
+#define SIP_REMOTECTL_CFG 0x8200000b
+
+/* Trust firmware version */
+#define ATF_VER_MAJOR(ver) (((ver) >> 16) & 0xffff)
+#define ATF_VER_MINOR(ver) (((ver) >> 0) & 0xffff)
+
+/* SIP_ACCESS_REG: read or write */
+#define SECURE_REG_RD 0x0
+#define SECURE_REG_WR 0x1
+
+/* Fiq debugger share memory: 8KB enough */
+#define FIQ_UARTDBG_PAGE_NUMS 2
+#define FIQ_UARTDBG_SHARE_MEM_SIZE ((FIQ_UARTDBG_PAGE_NUMS) * 4096)
+
+/* Error return code */
+#define IS_SIP_ERROR(x) (!!(x))
+
+#define SIP_RET_SUCCESS 0
+#define SIP_RET_SMC_UNKNOWN -1
+#define SIP_RET_NOT_SUPPORTED -2
+#define SIP_RET_INVALID_PARAMS -3
+#define SIP_RET_INVALID_ADDRESS -4
+#define SIP_RET_DENIED -5
+
+/* SIP_UARTDBG_CFG64 call types */
+#define UARTDBG_CFG_INIT 0xf0
+#define UARTDBG_CFG_OSHDL_TO_OS 0xf1
+#define UARTDBG_CFG_OSHDL_CPUSW 0xf3
+#define UARTDBG_CFG_OSHDL_DEBUG_ENABLE 0xf4
+#define UARTDBG_CFG_OSHDL_DEBUG_DISABLE 0xf5
+#define UARTDBG_CFG_PRINT_PORT 0xf7
+#define UARTDBG_CFG_FIQ_ENABEL 0xf8
+#define UARTDBG_CFG_FIQ_DISABEL 0xf9
+
+/* SIP_SUSPEND_MODE32 call types */
+#define SUSPEND_MODE_CONFIG 0x01
+#define WKUP_SOURCE_CONFIG 0x02
+#define PWM_REGULATOR_CONFIG 0x03
+#define GPIO_POWER_CONFIG 0x04
+#define SUSPEND_DEBUG_ENABLE 0x05
+#define APIOS_SUSPEND_CONFIG 0x06
+#define VIRTUAL_POWEROFF 0x07
+
+/* SIP_REMOTECTL_CFG call types */
+#define REMOTECTL_SET_IRQ 0xf0
+#define REMOTECTL_SET_PWM_CH 0xf1
+#define REMOTECTL_SET_PWRKEY 0xf2
+#define REMOTECTL_GET_WAKEUP_STATE 0xf3
+#define REMOTECTL_ENABLE 0xf4
+/* wakeup state */
+#define REMOTECTL_PWRKEY_WAKEUP 0xdeadbeaf
+
+/* Share mem page types */
+typedef enum {
+ SHARE_PAGE_TYPE_INVALID = 0,
+ SHARE_PAGE_TYPE_UARTDBG,
+ SHARE_PAGE_TYPE_MAX,
+} share_page_type_t;
+
+/*
+ * Rules: struct arm_smccc_res contains result and data, details:
+ *
+ * a0: error code(0: success, !0: error);
+ * a1~a3: data
+ */
+struct arm_smccc_res sip_smc_get_atf_version(void);
+struct arm_smccc_res sip_smc_get_sip_version(void);
+struct arm_smccc_res sip_smc_ddr_cfg(u32 arg0, u32 arg1, u32 arg2);
+struct arm_smccc_res sip_smc_request_share_mem(u32 page_num,
+ share_page_type_t page_type);
+struct arm_smccc_res sip_smc_mcu_el3fiq(u32 arg0, u32 arg1, u32 arg2);
+
+int sip_smc_set_suspend_mode(u32 ctrl, u32 config1, u32 config2);
+int sip_smc_virtual_poweroff(void);
+/***************************fiq debugger **************************************/
+void sip_fiq_debugger_enable_fiq(bool enable, uint32_t tgt_cpu);
+void sip_fiq_debugger_enable_debug(bool enable);
+int sip_fiq_debugger_uart_irq_tf_init(u32 irq_id, void *callback_fn);
+int sip_fiq_debugger_set_print_port(u32 port_phyaddr, u32 baudrate);
+int sip_fiq_debugger_request_share_memory(void);
+int sip_fiq_debugger_get_target_cpu(void);
+int sip_fiq_debugger_switch_cpu(u32 cpu);
+int sip_fiq_debugger_is_enabled(void);
+
+/* optee cpu_context */
+struct sm_nsec_ctx {
+ u32 usr_sp;
+ u32 usr_lr;
+ u32 irq_spsr;
+ u32 irq_sp;
+ u32 irq_lr;
+ u32 svc_spsr;
+ u32 svc_sp;
+ u32 svc_lr;
+ u32 abt_spsr;
+ u32 abt_sp;
+ u32 abt_lr;
+ u32 und_spsr;
+ u32 und_sp;
+ u32 und_lr;
+ u32 mon_lr;
+ u32 mon_spsr;
+ u32 r4;
+ u32 r5;
+ u32 r6;
+ u32 r7;
+ u32 r8;
+ u32 r9;
+ u32 r10;
+ u32 r11;
+ u32 r12;
+ u32 r0;
+ u32 r1;
+ u32 r2;
+ u32 r3;
+};
+
+#endif
--
2.26.2


+ 26
- 0
pkgs/os-specific/linux/kernel/nanopi/0013-arm64-dts-rockchip-setup-USB-type-c-port-as-dual-dat.patch View File

@@ -0,0 +1,26 @@
From 7d27663e4c2b112fbd035ea1b4d3ed074e98fbec Mon Sep 17 00:00:00 2001
From: Dan Johansen <strit@manjaro.org>
Date: Sun, 7 Jun 2020 17:25:45 +0200
Subject: [PATCH 1/2] arm64: dts: rockchip: setup USB type c port as dual data
role

---
arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
index cb0245d2226d..3c1823944524 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
@@ -698,7 +698,7 @@ fusb0: fusb30x@22 {
connector {
compatible = "usb-c-connector";
- data-role = "host";
+ data-role = "dual";
label = "USB-C";
op-sink-microwatt = <1000000>;
power-role = "dual";
--
2.26.2


+ 445
- 0
pkgs/os-specific/linux/kernel/nanopi/0015-add-dp-alt-mode-to-PBP.patch View File

@@ -0,0 +1,445 @@
From a1bbffd1cbb883be7fe3da1d09c29d57cfbeb2da Mon Sep 17 00:00:00 2001
From: Dan Johansen <strit@manjaro.org>
Date: Tue, 2 Jun 2020 20:20:29 +0200
Subject: [PATCH] add-dp-alt-mode-to-PBP

---
.../boot/dts/rockchip/rk3399-pinebook-pro.dts | 5 +
drivers/phy/rockchip/phy-rockchip-typec.c | 17 +++
drivers/usb/typec/altmodes/displayport.c | 58 +++++++-
drivers/usb/typec/bus.c | 8 +-
drivers/usb/typec/tcpm/tcpm.c | 139 +++++++++++++++++-
5 files changed, 221 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
index c49982dfd8fc..66cf08e8506f 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
@@ -374,6 +374,7 @@ mains_charger: dc-charger {
&cdn_dp {
status = "okay";
+ extcon = <&fusb0>;
};
&cpu_b0 {
@@ -708,6 +709,9 @@ connector {
<PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>;
try-power-role = "sink";
+ extcon-cables = <1 2 5 6 9 10 12 44>;
+ typec-altmodes = <0xff01 1 0x001c0000 1>;
+
ports {
#address-cells = <1>;
#size-cells = <0>;
@@ -958,6 +962,7 @@ spiflash: flash@0 {
};
&tcphy0 {
+ extcon = <&fusb0>;
status = "okay";
};
diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
index 24563160197f..f5b497b4b97e 100644
--- a/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -40,6 +40,7 @@
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -1160,6 +1161,22 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
dev_err(dev, "Invalid or missing extcon\n");
return PTR_ERR(tcphy->extcon);
}
+ } else {
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_SS);
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_SS);
+ extcon_set_property_capability(tcphy->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_SS);
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_set_property_capability(tcphy->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_sync(tcphy->extcon, EXTCON_USB);
+ extcon_sync(tcphy->extcon, EXTCON_USB_HOST);
+ extcon_sync(tcphy->extcon, EXTCON_DISP_DP);
}
pm_runtime_enable(dev);
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 0edfb89e04a8..40dd68c20159 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -9,6 +9,8 @@
*/
#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/usb/pd_vdo.h>
@@ -134,15 +136,53 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
return ret;
}
+static void dp_altmode_update_extcon(struct dp_altmode *dp, bool disconnect) {
+ const struct device *dev = &dp->port->dev;
+ struct extcon_dev* edev = NULL;
+
+ while (dev) {
+ edev = extcon_find_edev_by_node(dev->of_node);
+ if(!IS_ERR(edev)) {
+ break;
+ }
+ dev = dev->parent;
+ }
+
+ if (IS_ERR_OR_NULL(edev)) {
+ return;
+ }
+
+ if (disconnect || !dp->data.conf) {
+ extcon_set_state_sync(edev, EXTCON_DISP_DP, false);
+ } else {
+ union extcon_property_value extcon_true = { .intval = true };
+ extcon_set_state(edev, EXTCON_DISP_DP, true);
+ if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf) & DP_PIN_ASSIGN_MULTI_FUNC_MASK) {
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
+ extcon_set_property(edev, EXTCON_DISP_DP, EXTCON_PROP_USB_SS,
+ extcon_true);
+ } else {
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
+ }
+ extcon_sync(edev, EXTCON_DISP_DP);
+ extcon_set_state_sync(edev, EXTCON_USB, false);
+ }
+
+}
+
static int dp_altmode_configured(struct dp_altmode *dp)
{
int ret;
sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration");
- if (!dp->data.conf)
+ if (!dp->data.conf) {
+ dp_altmode_update_extcon(dp, true);
return typec_altmode_notify(dp->alt, TYPEC_STATE_USB,
&dp->data);
+ }
+
+ dp_altmode_update_extcon(dp, false);
ret = dp_altmode_notify(dp);
if (ret)
@@ -169,9 +209,11 @@ static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
if (ret) {
if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf))
dp_altmode_notify(dp);
- else
+ else {
+ dp_altmode_update_extcon(dp, true);
typec_altmode_notify(dp->alt, TYPEC_STATE_USB,
&dp->data);
+ }
}
return ret;
@@ -210,6 +252,8 @@ static void dp_altmode_work(struct work_struct *work)
case DP_STATE_EXIT:
if (typec_altmode_exit(dp->alt))
dev_err(&dp->alt->dev, "Exit Mode Failed!\n");
+ else
+ dp_altmode_update_extcon(dp, true);
break;
default:
break;
@@ -520,8 +564,14 @@ int dp_altmode_probe(struct typec_altmode *alt)
if (!(DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) &
DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo)) &&
!(DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) &
- DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo)))
- return -ENODEV;
+ DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo))) {
+ dev_err(&alt->dev, "No compatible pin configuration found:"\
+ "%04lx -> %04lx, %04lx <- %04lx",
+ DP_CAP_DFP_D_PIN_ASSIGN(port->vdo), DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo),
+ DP_CAP_UFP_D_PIN_ASSIGN(port->vdo), DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo));
+ return -ENODEV;
+ }
+
ret = sysfs_create_group(&alt->dev.kobj, &dp_altmode_group);
if (ret)
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index e8ddb81cb6df..cbc01d73739c 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -154,8 +154,14 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit);
*/
void typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
{
- struct typec_altmode *pdev = &to_altmode(adev)->partner->adev;
+ struct typec_altmode *pdev;
+ WARN_ONCE(!adev, "typec bus attention: adev is NULL!");
+ WARN_ONCE(!to_altmode(adev)->partner, "typec bus attention: partner is NULL!");
+ if(!adev || !to_altmode(adev)->partner) {
+ return;
+ }
+ pdev = &to_altmode(adev)->partner->adev;
if (pdev->ops && pdev->ops->attention)
pdev->ops->attention(pdev, vdo);
}
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 82b19ebd7838..6f00b17afc15 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -8,6 +8,7 @@
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/device.h>
+#include <linux/extcon-provider.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -322,6 +323,11 @@ struct tcpm_port {
/* port belongs to a self powered device */
bool self_powered;
+#ifdef CONFIG_EXTCON
+ struct extcon_dev *extcon;
+ unsigned int *extcon_cables;
+#endif
+
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
struct mutex logbuffer_lock; /* log buffer access lock */
@@ -607,6 +613,35 @@ static void tcpm_debugfs_exit(const struct tcpm_port *port) { }
#endif
+static void tcpm_update_extcon_data(struct tcpm_port *port, bool attached) {
+#ifdef CONFIG_EXTCON
+ unsigned int *capability = port->extcon_cables;
+ if (port->data_role == TYPEC_HOST) {
+ extcon_set_state(port->extcon, EXTCON_USB, false);
+ extcon_set_state(port->extcon, EXTCON_USB_HOST, attached);
+ } else {
+ extcon_set_state(port->extcon, EXTCON_USB, true);
+ extcon_set_state(port->extcon, EXTCON_USB_HOST, attached);
+ }
+ while (*capability != EXTCON_NONE) {
+ if (attached) {
+ union extcon_property_value val;
+ val.intval = (port->polarity == TYPEC_POLARITY_CC2);
+ extcon_set_property(port->extcon, *capability,
+ EXTCON_PROP_USB_TYPEC_POLARITY, val);
+ } else {
+ extcon_set_state(port->extcon, *capability, false);
+ }
+ extcon_sync(port->extcon, *capability);
+ capability++;
+ }
+ tcpm_log(port, "Extcon update (%s): %s, %s",
+ attached ? "attached" : "detached",
+ port->data_role == TYPEC_HOST ? "host" : "device",
+ port->polarity == TYPEC_POLARITY_CC1 ? "normal" : "flipped");
+#endif
+}
+
static int tcpm_pd_transmit(struct tcpm_port *port,
enum tcpm_transmit_type type,
const struct pd_message *msg)
@@ -834,6 +869,8 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached,
typec_set_data_role(port->typec_port, data);
typec_set_pwr_role(port->typec_port, role);
+ tcpm_update_extcon_data(port, attached);
+
return 0;
}
@@ -1044,7 +1081,7 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
paltmode->mode = i;
paltmode->vdo = le32_to_cpu(payload[i]);
- tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
+ tcpm_log(port, "Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
pmdata->altmodes, paltmode->svid,
paltmode->mode, paltmode->vdo);
@@ -1064,6 +1101,9 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)
if (!altmode)
tcpm_log(port, "Failed to register partner SVID 0x%04x",
modep->altmode_desc[i].svid);
+ else
+ tcpm_log(port, "Registered altmode 0x%04x", modep->altmode_desc[i].svid);
+
port->partner_altmode[i] = altmode;
}
}
@@ -1167,9 +1207,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
modep->svid_index++;
if (modep->svid_index < modep->nsvids) {
u16 svid = modep->svids[modep->svid_index];
+ tcpm_log(port, "More modes available, sending discover");
response[0] = VDO(svid, 1, CMD_DISCOVER_MODES);
rlen = 1;
} else {
+ tcpm_log(port, "Got all patner modes, registering");
tcpm_register_partner_altmodes(port);
}
break;
@@ -2693,6 +2735,7 @@ static int tcpm_src_attach(struct tcpm_port *port)
static void tcpm_typec_disconnect(struct tcpm_port *port)
{
if (port->connected) {
+ tcpm_update_extcon_data(port, false);
typec_unregister_partner(port->partner);
port->partner = NULL;
port->connected = false;
@@ -2750,6 +2793,8 @@ static void tcpm_detach(struct tcpm_port *port)
port->hard_reset_count = 0;
tcpm_reset_port(port);
+
+ tcpm_update_extcon_data(port, false);
}
static void tcpm_src_detach(struct tcpm_port *port)
@@ -4424,6 +4469,64 @@ void tcpm_tcpc_reset(struct tcpm_port *port)
}
EXPORT_SYMBOL_GPL(tcpm_tcpc_reset);
+unsigned int default_supported_cables[] = {
+ EXTCON_NONE
+};
+
+static int tcpm_fw_get_caps_late(struct tcpm_port *port,
+ struct fwnode_handle *fwnode)
+{
+ int ret, i;
+ ret = fwnode_property_count_u32(fwnode, "typec-altmodes");
+ if (ret > 0) {
+ u32 *props;
+ if (ret % 4) {
+ dev_err(port->dev, "Length of typec altmode array must be divisible by 4");
+ return -EINVAL;
+ }
+
+ props = devm_kzalloc(port->dev, sizeof(u32) * ret, GFP_KERNEL);
+ if (!props) {
+ dev_err(port->dev, "Failed to allocate memory for altmode properties");
+ return -ENOMEM;
+ }
+
+ if(fwnode_property_read_u32_array(fwnode, "typec-altmodes", props, ret) < 0) {
+ dev_err(port->dev, "Failed to read altmodes from port");
+ return -EINVAL;
+ }
+
+ i = 0;
+ while (ret > 0 && i < ARRAY_SIZE(port->port_altmode)) {
+ struct typec_altmode *alt;
+ struct typec_altmode_desc alt_desc = {
+ .svid = props[i * 4],
+ .mode = props[i * 4 + 1],
+ .vdo = props[i * 4 + 2],
+ .roles = props[i * 4 + 3],
+ };
+
+
+ tcpm_log(port, "Adding altmode SVID: 0x%04x, mode: %d, vdo: %u, role: %d",
+ alt_desc.svid, alt_desc.mode, alt_desc.vdo, alt_desc.roles);
+ alt = typec_port_register_altmode(port->typec_port,
+ &alt_desc);
+ if (IS_ERR(alt)) {
+ tcpm_log(port,
+ "%s: failed to register port alternate mode 0x%x",
+ dev_name(port->dev), alt_desc.svid);
+ break;
+ }
+ typec_altmode_set_drvdata(alt, port);
+ alt->ops = &tcpm_altmode_ops;
+ port->port_altmode[i] = alt;
+ i++;
+ ret -= 4;
+ }
+ }
+ return 0;
+}
+
static int tcpm_fw_get_caps(struct tcpm_port *port,
struct fwnode_handle *fwnode)
{
@@ -4434,6 +4537,23 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
if (!fwnode)
return -EINVAL;
+#ifdef CONFIG_EXTCON
+ ret = fwnode_property_count_u32(fwnode, "extcon-cables");
+ if (ret > 0) {
+ port->extcon_cables = devm_kzalloc(port->dev, sizeof(u32) * ret, GFP_KERNEL);
+ if (!port->extcon_cables) {
+ dev_err(port->dev, "Failed to allocate memory for extcon cable types. "\
+ "Using default tyes");
+ goto extcon_default;
+ }
+ fwnode_property_read_u32_array(fwnode, "extcon-cables", port->extcon_cables, ret);
+ } else {
+extcon_default:
+ dev_info(port->dev, "No cable types defined, using default cables");
+ port->extcon_cables = default_supported_cables;
+ }
+#endif
+
/* USB data support is optional */
ret = fwnode_property_read_string(fwnode, "data-role", &cap_str);
if (ret == 0) {
@@ -4766,6 +4886,17 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
goto out_destroy_wq;
port->try_role = port->typec_caps.prefer_role;
+#ifdef CONFIG_EXTCON
+ port->extcon = devm_extcon_dev_allocate(dev, port->extcon_cables);
+ if (IS_ERR(port->extcon)) {
+ dev_err(dev, "Failed to allocate extcon device: %ld", PTR_ERR(port->extcon));
+ goto out_destroy_wq;
+ }
+ if((err = devm_extcon_dev_register(dev, port->extcon))) {
+ dev_err(dev, "Failed to register extcon device: %d", err);
+ goto out_destroy_wq;
+ }
+#endif
port->typec_caps.fwnode = tcpc->fwnode;
port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */
@@ -4793,6 +4924,12 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
goto out_role_sw_put;
}
+ err = tcpm_fw_get_caps_late(port, tcpc->fwnode);
+ if (err < 0) {
+ dev_err(dev, "Failed to get altmodes from fwnode");
+ goto out_destroy_wq;
+ }
+
mutex_lock(&port->lock);
tcpm_init(port);
mutex_unlock(&port->lock);
--
2.26.2


+ 26
- 0
pkgs/os-specific/linux/kernel/nanopi/RK3399-enable_1.5_2.0_ghz_cpufreq_opp.patch View File

@@ -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 {

+ 84
- 0
pkgs/os-specific/linux/kernel/nanopi/add-board-nanopi-m4v2.patch View File

@@ -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>;
+};

+ 83
- 0
pkgs/os-specific/linux/kernel/nanopi/add-csgpio-to-rockchip-spi.patch View File

@@ -0,0 +1,83 @@
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 2cc6d99..19e05ad 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -6,6 +6,7 @@
#include <linux/clk.h>
#include <linux/dmaengine.h>
+#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -185,6 +186,10 @@ struct rockchip_spi {
bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM];
};
+struct rockchip_spi_data {
+ bool cs_gpio_requested;
+};
+
static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable)
{
writel_relaxed((enable ? 1U : 0U), rs->regs + ROCKCHIP_SPI_SSIENR);
@@ -455,6 +460,50 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
return 1;
}
+static int rockchip_spi_setup(struct spi_device *spi)
+{
+ int ret = 0;
+ unsigned long flags = (spi->mode & SPI_CS_HIGH) ?
+ GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH;
+ struct rockchip_spi_data *data = spi_get_ctldata(spi);
+
+ if (!gpio_is_valid(spi->cs_gpio))
+ return 0;
+
+ if (!data) {
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ spi_set_ctldata(spi, data);
+ }
+
+ if (!data->cs_gpio_requested) {
+ ret = gpio_request_one(spi->cs_gpio, flags,
+ dev_name(&spi->dev));
+ if (!ret)
+ data->cs_gpio_requested = 1;
+ } else
+ ret = gpio_direction_output(spi->cs_gpio, flags);
+
+ if (ret < 0)
+ dev_err(&spi->dev, "Failed to setup cs gpio(%d): %d\n",
+ spi->cs_gpio, ret);
+
+ return ret;
+}
+
+static void rockchip_spi_cleanup(struct spi_device *spi)
+{
+ struct rockchip_spi_data *data = spi_get_ctldata(spi);
+
+ if (data) {
+ if (data->cs_gpio_requested)
+ gpio_free(spi->cs_gpio);
+ kfree(data);
+ spi_set_ctldata(spi, NULL);
+ }
+}
+
static void rockchip_spi_config(struct rockchip_spi *rs,
struct spi_device *spi, struct spi_transfer *xfer,
bool use_dma)
@@ -683,6 +732,8 @@ static int rockchip_spi_probe(struct platform_device *pdev)
ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT);
ctlr->set_cs = rockchip_spi_set_cs;
+ ctlr->setup = rockchip_spi_setup;
+ ctlr->cleanup = rockchip_spi_cleanup;
ctlr->transfer_one = rockchip_spi_transfer_one;
ctlr->max_transfer_size = rockchip_spi_max_transfer_size;
ctlr->handle_err = rockchip_spi_handle_err;

+ 4047
- 0
pkgs/os-specific/linux/kernel/nanopi/add-fusb30x-driver.patch View File

@@ -0,0 +1,4047 @@
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 1abf76be2..7ad8b090c 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -92,6 +92,8 @@ source "drivers/staging/fbtft/Kconfig"
source "drivers/staging/fsl-dpaa2/Kconfig"
+source "drivers/staging/fusb30x/Kconfig"
+
source "drivers/staging/wilc1000/Kconfig"
source "drivers/staging/most/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index ab0cbe881..2e308d901 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_UNISYSSPAR) += unisys/
obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
obj-$(CONFIG_FB_TFT) += fbtft/
obj-$(CONFIG_FSL_DPAA2) += fsl-dpaa2/
+obj-$(CONFIG_FUSB_30X) += fusb30x/
obj-$(CONFIG_WILC1000) += wilc1000/
obj-$(CONFIG_MOST) += most/
obj-$(CONFIG_KS7010) += ks7010/
diff --git a/drivers/staging/fusb30x/Kconfig b/drivers/staging/fusb30x/Kconfig
new file mode 100644
index 000000000..5bb75270f
--- /dev/null
+++ b/drivers/staging/fusb30x/Kconfig
@@ -0,0 +1,10 @@
+config FUSB_30X
+ tristate "Fairchild FUSB30X Type-C chip driver"
+ depends on I2C
+ help
+ This is a driver for the Fairchild FUSB302 Type-C chip. It supports
+ USB Type-C PD functionality controlled using I2C.
+
+ This driver supports extcon reporting not yet implemented in the
+ mainline FUSB302 driver.
+
diff --git a/drivers/staging/fusb30x/Makefile b/drivers/staging/fusb30x/Makefile
new file mode 100644
index 000000000..1c8e35df3
--- /dev/null
+++ b/drivers/staging/fusb30x/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_FUSB_30X) += fusb30x.o
diff --git a/drivers/staging/fusb30x/fusb30x.c b/drivers/staging/fusb30x/fusb30x.c
new file mode 100644
index 000000000..56d22648c
--- /dev/null
+++ b/drivers/staging/fusb30x/fusb30x.c
@@ -0,0 +1,3434 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Zain Wang <zain.wang@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Some ideas are from chrome ec and fairchild GPL fusb302 driver.
+ */
+
+#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/power_supply.h>
+
+#include "fusb30x.h"
+
+#define FUSB302_MAX_REG (FUSB_REG_FIFO + 50)
+#define FUSB_MS_TO_NS(x) ((s64)x * 1000 * 1000)
+
+#define TYPEC_CC_VOLT_OPEN 0
+#define TYPEC_CC_VOLT_RA 1
+#define TYPEC_CC_VOLT_RD 2
+#define TYPEC_CC_VOLT_RP 3
+
+#define EVENT_CC BIT(0)
+#define EVENT_RX BIT(1)
+#define EVENT_TX BIT(2)
+#define EVENT_REC_RESET BIT(3)
+#define EVENT_WORK_CONTINUE BIT(5)
+#define EVENT_TIMER_MUX BIT(6)
+#define EVENT_TIMER_STATE BIT(7)
+#define EVENT_DELAY_CC BIT(8)
+#define FLAG_EVENT (EVENT_RX | EVENT_TIMER_MUX | \
+ EVENT_TIMER_STATE)
+
+#define PACKET_IS_CONTROL_MSG(header, type) \
+ (PD_HEADER_CNT(header) == 0 && \
+ PD_HEADER_TYPE(header) == type)
+
+#define PACKET_IS_DATA_MSG(header, type) \
+ (PD_HEADER_CNT(header) != 0 && \
+ PD_HEADER_TYPE(header) == type)
+
+/*
+ * DisplayPort modes capabilities
+ * -------------------------------
+ * <31:24> : Reserved (always 0).
+ * <23:16> : UFP_D pin assignment supported
+ * <15:8> : DFP_D pin assignment supported
+ * <7> : USB 2.0 signaling (0b=yes, 1b=no)
+ * <6> : Plug | Receptacle (0b == plug, 1b == receptacle)
+ * <5:2> : xxx1: Supports DPv1.3, xx1x Supports USB Gen 2 signaling
+ * Other bits are reserved.
+ * <1:0> : signal direction ( 00b=rsv, 01b=sink, 10b=src 11b=both )
+ */
+#define PD_DP_PIN_CAPS(x) ((((x) >> 6) & 0x1) ? (((x) >> 16) & 0x3f) \
+ : (((x) >> 8) & 0x3f))
+#define PD_DP_SIGNAL_GEN2(x) (((x) >> 3) & 0x1)
+
+#define MODE_DP_PIN_A BIT(0)
+#define MODE_DP_PIN_B BIT(1)
+#define MODE_DP_PIN_C BIT(2)
+#define MODE_DP_PIN_D BIT(3)
+#define MODE_DP_PIN_E BIT(4)
+#define MODE_DP_PIN_F BIT(5)
+
+/* Pin configs B/D/F support multi-function */
+#define MODE_DP_PIN_MF_MASK (MODE_DP_PIN_B | MODE_DP_PIN_D | MODE_DP_PIN_F)
+/* Pin configs A/B support BR2 signaling levels */
+#define MODE_DP_PIN_BR2_MASK (MODE_DP_PIN_A | MODE_DP_PIN_B)
+/* Pin configs C/D/E/F support DP signaling levels */
+#define MODE_DP_PIN_DP_MASK (MODE_DP_PIN_C | MODE_DP_PIN_D | \
+ MODE_DP_PIN_E | MODE_DP_PIN_F)
+
+/*
+ * DisplayPort Status VDO
+ * ----------------------
+ * <31:9> : Reserved (always 0).
+ * <8> : IRQ_HPD : 1 == irq arrived since last message otherwise 0.
+ * <7> : HPD state : 0 = HPD_LOW, 1 == HPD_HIGH
+ * <6> : Exit DP Alt mode: 0 == maintain, 1 == exit
+ * <5> : USB config : 0 == maintain current, 1 == switch to USB from DP
+ * <4> : Multi-function preference : 0 == no pref, 1 == MF preferred.
+ * <3> : enabled : is DPout on/off.
+ * <2> : power low : 0 == normal or LPM disabled, 1 == DP disabled for LPM
+ * <1:0> : connect status : 00b == no (DFP|UFP)_D is connected or disabled.
+ * 01b == DFP_D connected, 10b == UFP_D connected, 11b == both.
+ */
+#define PD_VDO_DPSTS_HPD_IRQ(x) (((x) >> 8) & 0x1)
+#define PD_VDO_DPSTS_HPD_LVL(x) (((x) >> 7) & 0x1)
+#define PD_VDO_DPSTS_MF_PREF(x) (((x) >> 4) & 0x1)
+
+static u8 fusb30x_port_used;
+static struct fusb30x_chip *fusb30x_port_info[256];
+
+static bool is_write_reg(struct device *dev, unsigned int reg)
+{
+ if (reg >= FUSB_REG_FIFO)
+ return true;
+ else
+ return ((reg < (FUSB_REG_CONTROL4 + 1)) && (reg > 0x01)) ?
+ true : false;
+}
+
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ if (reg > FUSB_REG_CONTROL4)
+ return true;
+
+ switch (reg) {
+ case FUSB_REG_CONTROL0:
+ case FUSB_REG_CONTROL1:
+ case FUSB_REG_CONTROL3:
+ case FUSB_REG_RESET:
+ return true;
+ }
+ return false;
+}
+
+struct regmap_config fusb302_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = is_write_reg,
+ .volatile_reg = is_volatile_reg,
+ .max_register = FUSB302_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void dump_notify_info(struct fusb30x_chip *chip)
+{
+ dev_dbg(chip->dev, "port %d\n", chip->port_num);
+ dev_dbg(chip->dev, "orientation %d\n", chip->notify.orientation);
+ dev_dbg(chip->dev, "power_role %d\n", chip->notify.power_role);
+ dev_dbg(chip->dev, "data_role %d\n", chip->notify.data_role);
+ dev_dbg(chip->dev, "cc %d\n", chip->notify.is_cc_connected);
+ dev_dbg(chip->dev, "pd %d\n", chip->notify.is_pd_connected);
+ dev_dbg(chip->dev, "enter_mode %d\n", chip->notify.is_enter_mode);
+ dev_dbg(chip->dev, "pin support %d\n",
+ chip->notify.pin_assignment_support);
+ dev_dbg(chip->dev, "pin def %d\n", chip->notify.pin_assignment_def);
+ dev_dbg(chip->dev, "attention %d\n", chip->notify.attention);
+}
+
+static const unsigned int fusb302_cable[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_CHG_USB_SDP,
+ EXTCON_CHG_USB_CDP,
+ EXTCON_CHG_USB_DCP,
+ EXTCON_CHG_USB_SLOW,
+ EXTCON_CHG_USB_FAST,
+ EXTCON_DISP_DP,
+ EXTCON_NONE,
+};
+
+static void fusb_set_pos_power(struct fusb30x_chip *chip, int max_vol,
+ int max_cur)
+{
+ int i;
+ int pos_find;
+ int tmp;
+
+ pos_find = 0;
+ for (i = PD_HEADER_CNT(chip->rec_head) - 1; i >= 0; i--) {
+ switch (CAP_POWER_TYPE(chip->rec_load[i])) {
+ case 0:
+ /* Fixed Supply */
+ if ((CAP_FPDO_VOLTAGE(chip->rec_load[i]) * 50) <=
+ max_vol &&
+ (CAP_FPDO_CURRENT(chip->rec_load[i]) * 10) <=
+ max_cur) {
+ chip->pos_power = i + 1;
+ tmp = CAP_FPDO_VOLTAGE(chip->rec_load[i]);
+ chip->pd_output_vol = tmp * 50;
+ tmp = CAP_FPDO_CURRENT(chip->rec_load[i]);
+ chip->pd_output_cur = tmp * 10;
+ pos_find = 1;
+ }
+ break;
+ case 1:
+ /* Battery */
+ if ((CAP_VPDO_VOLTAGE(chip->rec_load[i]) * 50) <=
+ max_vol &&
+ (CAP_VPDO_CURRENT(chip->rec_load[i]) * 10) <=
+ max_cur) {
+ chip->pos_power = i + 1;
+ tmp = CAP_VPDO_VOLTAGE(chip->rec_load[i]);
+ chip->pd_output_vol = tmp * 50;
+ tmp = CAP_VPDO_CURRENT(chip->rec_load[i]);
+ chip->pd_output_cur = tmp * 10;
+ pos_find = 1;
+ }
+ break;
+ default:
+ /* not meet battery caps */
+ break;
+ }
+ if (pos_find)
+ break;
+ }
+}
+
+static int fusb302_set_pos_power_by_charge_ic(struct fusb30x_chip *chip)
+{
+ struct power_supply *psy = NULL;
+ union power_supply_propval val;
+ enum power_supply_property psp;
+ int max_vol, max_cur;
+
+ max_vol = 0;
+ max_cur = 0;
+ psy = power_supply_get_by_phandle(chip->dev->of_node, "charge-dev");
+ if (!psy || IS_ERR(psy))
+ return -1;
+
+ psp = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX;
+ if (power_supply_get_property(psy, psp, &val) == 0)
+ max_vol = val.intval / 1000;
+
+ psp = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
+ if (power_supply_get_property(psy, psp, &val) == 0)
+ max_cur = val.intval / 1000;
+
+ if (max_vol > 0 && max_cur > 0)
+ fusb_set_pos_power(chip, max_vol, max_cur);
+
+ return 0;
+}
+
+void fusb_irq_disable(struct fusb30x_chip *chip)
+{
+ unsigned long irqflags = 0;
+
+ spin_lock_irqsave(&chip->irq_lock, irqflags);
+ if (chip->enable_irq) {
+ disable_irq_nosync(chip->gpio_int_irq);
+ chip->enable_irq = 0;
+ } else {
+ dev_warn(chip->dev, "irq have already disabled\n");
+ }
+ spin_unlock_irqrestore(&chip->irq_lock, irqflags);
+}
+
+void fusb_irq_enable(struct fusb30x_chip *chip)
+{
+ unsigned long irqflags = 0;
+
+ spin_lock_irqsave(&chip->irq_lock, irqflags);
+ if (!chip->enable_irq) {
+ enable_irq(chip->gpio_int_irq);
+ chip->enable_irq = 1;
+ }
+ spin_unlock_irqrestore(&chip->irq_lock, irqflags);
+}
+
+static void platform_fusb_notify(struct fusb30x_chip *chip)
+{
+ bool plugged = false, flip = false, dfp = false, ufp = false,
+ dp = false, usb_ss = false, hpd = false;
+ union extcon_property_value property;
+
+ if (chip->notify.is_cc_connected)
+ chip->notify.orientation =
+ (chip->cc_polarity == TYPEC_POLARITY_CC1) ?
+ CC1 : CC2;
+
+ /* avoid notify repeated */
+ if (memcmp(&chip->notify, &chip->notify_cmp,
+ sizeof(struct notify_info))) {
+ dump_notify_info(chip);
+ chip->notify.attention = false;
+ memcpy(&chip->notify_cmp, &chip->notify,
+ sizeof(struct notify_info));
+
+ plugged = chip->notify.is_cc_connected ||
+ chip->notify.is_pd_connected;
+ if (chip->notify.orientation != NONE)
+ flip = (chip->notify.orientation == CC1) ? false : true;
+ dp = chip->notify.is_enter_mode;
+
+ if (dp) {
+ dfp = true;
+ usb_ss = (chip->notify.pin_assignment_def &
+ MODE_DP_PIN_MF_MASK) ? true : false;
+ hpd = GET_DP_STATUS_HPD(chip->notify.dp_status);
+ } else if (chip->notify.data_role) {
+ dfp = true;
+ usb_ss = true;
+ } else if (plugged) {
+ ufp = true;
+ usb_ss = true;
+ }
+
+ property.intval = flip;
+ extcon_set_property(chip->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_TYPEC_POLARITY, property);
+ extcon_set_property(chip->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY, property);
+ extcon_set_property(chip->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_TYPEC_POLARITY, property);
+
+ property.intval = usb_ss;
+ extcon_set_property(chip->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_SS, property);
+ extcon_set_property(chip->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_SS, property);
+ extcon_set_property(chip->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_SS, property);
+ extcon_set_state(chip->extcon, EXTCON_USB, ufp);
+ extcon_set_state(chip->extcon, EXTCON_USB_HOST, dfp);
+ extcon_set_state(chip->extcon, EXTCON_DISP_DP, dp && hpd);
+ extcon_sync(chip->extcon, EXTCON_USB);
+ extcon_sync(chip->extcon, EXTCON_USB_HOST);
+ extcon_sync(chip->extcon, EXTCON_DISP_DP);
+ if (chip->notify.power_role == POWER_ROLE_SINK &&
+ chip->notify.is_pd_connected &&
+ chip->pd_output_vol > 0 && chip->pd_output_cur > 0) {
+ extcon_set_state(chip->extcon, EXTCON_CHG_USB_FAST, true);
+ property.intval =
+ (chip->pd_output_cur << 15 |
+ chip->pd_output_vol);
+ extcon_set_property(chip->extcon, EXTCON_CHG_USB_FAST,
+ EXTCON_PROP_USB_TYPEC_POLARITY,
+ property);
+ extcon_sync(chip->extcon, EXTCON_CHG_USB_FAST);
+ }
+ }
+}
+
+static bool platform_get_device_irq_state(struct fusb30x_chip *chip)
+{
+ return !gpiod_get_value(chip->gpio_int);
+}
+
+static void fusb_timer_start(struct hrtimer *timer, int ms)
+{
+ ktime_t ktime;
+
+ ktime = ktime_set(0, FUSB_MS_TO_NS(ms));
+ hrtimer_start(timer, ktime, HRTIMER_MODE_REL);
+}
+
+static void platform_set_vbus_lvl_enable(struct fusb30x_chip *chip, int vbus_5v,
+ int vbus_other)
+{
+ bool gpio_vbus_value = false;
+
+ gpio_vbus_value = gpiod_get_value(chip->gpio_vbus_5v);
+ if (chip->gpio_vbus_5v) {
+ gpiod_set_raw_value(chip->gpio_vbus_5v, vbus_5v);
+ }
+
+ if (chip->gpio_vbus_other)
+ gpiod_set_raw_value(chip->gpio_vbus_5v, vbus_other);
+
+ if (chip->gpio_discharge && !vbus_5v && gpio_vbus_value) {
+ gpiod_set_value(chip->gpio_discharge, 1);
+ msleep(20);
+ gpiod_set_value(chip->gpio_discharge, 0);
+ }
+}
+
+static void set_state(struct fusb30x_chip *chip, enum connection_state state)
+{
+ dev_dbg(chip->dev, "port %d, state %d\n", chip->port_num, state);
+ if (!state)
+ dev_info(chip->dev, "PD disabled\n");
+ chip->conn_state = state;
+ chip->sub_state = 0;
+ chip->val_tmp = 0;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+}
+
+static int tcpm_get_message(struct fusb30x_chip *chip)
+{
+ u8 buf[32];
+ int len;
+
+ do {
+ regmap_raw_read(chip->regmap, FUSB_REG_FIFO, buf, 3);
+ chip->rec_head = (buf[1] & 0xff) | ((buf[2] << 8) & 0xff00);
+
+ len = PD_HEADER_CNT(chip->rec_head) << 2;
+ regmap_raw_read(chip->regmap, FUSB_REG_FIFO, buf, len + 4);
+ /* ignore good_crc message */
+ } while (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_GOODCRC));
+
+ memcpy(chip->rec_load, buf, len);
+
+ return 0;
+}
+
+static void fusb302_flush_rx_fifo(struct fusb30x_chip *chip)
+{
+ regmap_write(chip->regmap, FUSB_REG_CONTROL1, CONTROL1_RX_FLUSH);
+}
+
+static int tcpm_get_cc(struct fusb30x_chip *chip, int *CC1, int *CC2)
+{
+ u32 val;
+ int *CC_MEASURE;
+ u32 store;
+
+ *CC1 = TYPEC_CC_VOLT_OPEN;
+ *CC2 = TYPEC_CC_VOLT_OPEN;
+
+ if (chip->cc_state & CC_STATE_TOGSS_CC1)
+ CC_MEASURE = CC1;
+ else
+ CC_MEASURE = CC2;
+
+ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) {
+ regmap_read(chip->regmap, FUSB_REG_SWITCHES0, &store);
+ /* measure cc1 first */
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 |
+ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 |
+ SWITCHES0_PDWN1 | SWITCHES0_PDWN2,
+ SWITCHES0_PDWN1 | SWITCHES0_PDWN2 |
+ SWITCHES0_MEAS_CC1);
+ usleep_range(250, 300);
+
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+ val &= STATUS0_BC_LVL;
+ *CC1 = val ? TYPEC_CC_VOLT_RP : TYPEC_CC_VOLT_OPEN;
+
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 |
+ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 |
+ SWITCHES0_PDWN1 | SWITCHES0_PDWN2,
+ SWITCHES0_PDWN1 | SWITCHES0_PDWN2 |
+ SWITCHES0_MEAS_CC2);
+ usleep_range(250, 300);
+
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+ val &= STATUS0_BC_LVL;
+ *CC2 = val ? TYPEC_CC_VOLT_RP : TYPEC_CC_VOLT_OPEN;
+
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2,
+ store);
+ } else {
+ regmap_read(chip->regmap, FUSB_REG_SWITCHES0, &store);
+ val = store;
+ val &= ~(SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 |
+ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2);
+ if (chip->cc_state & CC_STATE_TOGSS_CC1) {
+ val |= SWITCHES0_MEAS_CC1 | SWITCHES0_PU_EN1;
+ } else {
+ val |= SWITCHES0_MEAS_CC2 | SWITCHES0_PU_EN2;
+ }
+ regmap_write(chip->regmap, FUSB_REG_SWITCHES0, val);
+
+ regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_high);
+ usleep_range(250, 300);
+
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+ if (val & STATUS0_COMP) {
+ int retry = 3;
+ int comp_times = 0;
+
+ while (retry--) {
+ regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_high);
+ usleep_range(250, 300);
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+ if (val & STATUS0_COMP) {
+ comp_times++;
+ if (comp_times == 3) {
+ *CC_MEASURE = TYPEC_CC_VOLT_OPEN;
+ regmap_write(chip->regmap, FUSB_REG_SWITCHES0, store);
+ }
+ }
+ }
+ } else {
+ regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_low);
+ regmap_read(chip->regmap, FUSB_REG_MEASURE, &val);
+ usleep_range(250, 300);
+
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+
+ if (val & STATUS0_COMP)
+ *CC_MEASURE = TYPEC_CC_VOLT_RD;
+ else
+ *CC_MEASURE = TYPEC_CC_VOLT_RA;
+ }
+ regmap_write(chip->regmap, FUSB_REG_SWITCHES0, store);
+ regmap_write(chip->regmap, FUSB_REG_MEASURE,
+ chip->cc_meas_high);
+ }
+
+ return 0;
+}
+
+static void tcpm_set_cc_pull_mode(struct fusb30x_chip *chip, enum CC_MODE mode)
+{
+ u8 val;
+
+ switch (mode) {
+ case CC_PULL_UP:
+ if (chip->cc_polarity == TYPEC_POLARITY_CC1)
+ val = SWITCHES0_PU_EN1;
+ else
+ val = SWITCHES0_PU_EN2;
+ break;
+ case CC_PULL_DOWN:
+ val = SWITCHES0_PDWN1 | SWITCHES0_PDWN2;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 |
+ SWITCHES0_PDWN1 | SWITCHES0_PDWN2,
+ val);
+
+ if (chip->cc_meas_high && mode == CC_PULL_UP)
+ regmap_write(chip->regmap, FUSB_REG_MEASURE,
+ chip->cc_meas_high);
+}
+
+static int tcpm_set_cc(struct fusb30x_chip *chip, enum role_mode mode)
+{
+ switch (mode) {
+ case ROLE_MODE_DFP:
+ tcpm_set_cc_pull_mode(chip, CC_PULL_UP);
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+ CONTROL2_MODE | CONTROL2_TOG_RD_ONLY,
+ CONTROL2_MODE_DFP | CONTROL2_TOG_RD_ONLY);
+ break;
+ case ROLE_MODE_UFP:
+ tcpm_set_cc_pull_mode(chip, CC_PULL_UP);
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+ CONTROL2_MODE | CONTROL2_TOG_RD_ONLY,
+ CONTROL2_MODE_UFP);
+ break;
+ case ROLE_MODE_DRP:
+ tcpm_set_cc_pull_mode(chip, CC_PULL_NONE);
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+ CONTROL2_MODE | CONTROL2_TOG_RD_ONLY,
+ CONTROL2_MODE_DRP | CONTROL2_TOG_RD_ONLY);
+ break;
+ default:
+ dev_err(chip->dev, "%s: Unsupport cc mode %d\n",
+ __func__, mode);
+ return -EINVAL;
+ break;
+ }
+
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, CONTROL2_TOGGLE,
+ CONTROL2_TOGGLE);
+
+ return 0;
+}
+
+static int tcpm_set_rx_enable(struct fusb30x_chip *chip, int enable)
+{
+ u8 val = 0;
+
+ if (enable) {
+ if (chip->cc_polarity == TYPEC_POLARITY_CC1)
+ val |= SWITCHES0_MEAS_CC1;
+ else
+ val |= SWITCHES0_MEAS_CC2;
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2,
+ val);
+ fusb302_flush_rx_fifo(chip);
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1,
+ SWITCHES1_AUTO_CRC, SWITCHES1_AUTO_CRC);
+ } else {
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2,
+ 0);
+ regmap_update_bits(chip->regmap,
+ FUSB_REG_SWITCHES1, SWITCHES1_AUTO_CRC, 0);
+ }
+
+ return 0;
+}
+
+static int tcpm_set_msg_header(struct fusb30x_chip *chip)
+{
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1,
+ SWITCHES1_POWERROLE | SWITCHES1_DATAROLE,
+ (chip->notify.power_role << 7) |
+ (chip->notify.data_role << 4));
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1,
+ SWITCHES1_SPECREV, 2 << 5);
+ return 0;
+}
+
+static int tcpm_set_polarity(struct fusb30x_chip *chip,
+ enum typec_cc_polarity polarity)
+{
+ u8 val = 0;
+
+ if (chip->vconn_enabled) {
+ if (polarity)
+ val |= SWITCHES0_VCONN_CC1;
+ else
+ val |= SWITCHES0_VCONN_CC2;
+ }
+
+ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) {
+ if (polarity == TYPEC_POLARITY_CC1)
+ val |= SWITCHES0_MEAS_CC1;
+ else
+ val |= SWITCHES0_MEAS_CC2;
+ } else {
+ if (polarity == TYPEC_POLARITY_CC1)
+ val |= SWITCHES0_MEAS_CC1 | SWITCHES0_PU_EN1;
+ else
+ val |= SWITCHES0_MEAS_CC2 | SWITCHES0_PU_EN2;
+ }
+
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2 |
+ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 |
+ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2,
+ val);
+
+ val = 0;
+ if (polarity == TYPEC_POLARITY_CC1)
+ val |= SWITCHES1_TXCC1;
+ else
+ val |= SWITCHES1_TXCC2;
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1,
+ SWITCHES1_TXCC1 | SWITCHES1_TXCC2,
+ val);
+
+ chip->cc_polarity = polarity;
+
+ return 0;
+}
+
+static int tcpm_set_vconn(struct fusb30x_chip *chip, int enable)
+{
+ u8 val = 0;
+
+ if (enable) {
+ if (chip->cc_polarity == TYPEC_POLARITY_CC1)
+ val = SWITCHES0_VCONN_CC2;
+ else
+ val = SWITCHES0_VCONN_CC1;
+ }
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2,
+ val);
+ chip->vconn_enabled = (bool)enable;
+ return 0;
+}
+
+static void fusb302_pd_reset(struct fusb30x_chip *chip)
+{
+ regmap_write(chip->regmap, FUSB_REG_RESET, RESET_PD_RESET);
+ regmap_reinit_cache(chip->regmap, &fusb302_regmap_config);
+}
+
+static void tcpm_select_rp_value(struct fusb30x_chip *chip, u32 rp)
+{
+ u32 control0_reg;
+
+ regmap_read(chip->regmap, FUSB_REG_CONTROL0, &control0_reg);
+
+ control0_reg &= ~CONTROL0_HOST_CUR;
+ /*
+ * according to the host current, the compare value is different
+ * Fusb302 datasheet Table 3
+ */
+ switch (rp) {
+ /*
+ * host pull up current is 80ua , high voltage is 1.596v,
+ * low is 0.21v
+ */
+ case TYPEC_RP_USB:
+ chip->cc_meas_high = 0x26;
+ chip->cc_meas_low = 0x5;
+ control0_reg |= CONTROL0_HOST_CUR_USB;
+ break;
+ /*
+ * host pull up current is 330ua , high voltage is 2.604v,
+ * low is 0.798v
+ */
+ case TYPEC_RP_3A0:
+ chip->cc_meas_high = 0x3e;
+ chip->cc_meas_low = 0x13;
+ control0_reg |= CONTROL0_HOST_CUR_3A0;
+ break;
+ /*
+ * host pull up current is 180ua , high voltage is 1.596v,
+ * low is 0.42v
+ */
+ case TYPEC_RP_1A5:
+ default:
+ chip->cc_meas_high = 0x26;
+ chip->cc_meas_low = 0xa;
+ control0_reg |= CONTROL0_HOST_CUR_1A5;
+ break;
+ }
+
+ regmap_write(chip->regmap, FUSB_REG_CONTROL0, control0_reg);
+}
+
+static int tcpm_check_vbus(struct fusb30x_chip *chip)
+{
+ u32 val;
+
+ /* Read status register */
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+
+ return (val & STATUS0_VBUSOK) ? 1 : 0;
+}
+
+static void tcpm_init(struct fusb30x_chip *chip)
+{
+ u8 val;
+ u32 tmp;
+
+ regmap_read(chip->regmap, FUSB_REG_DEVICEID, &tmp);
+ chip->chip_id = (u8)tmp;
+ platform_set_vbus_lvl_enable(chip, 0, 0);
+ chip->notify.is_cc_connected = false;
+ chip->cc_state = 0;
+
+ /* restore default settings */
+ regmap_update_bits(chip->regmap, FUSB_REG_RESET, RESET_SW_RESET,
+ RESET_SW_RESET);
+ fusb302_pd_reset(chip);
+ /* set auto_retry and number of retries */
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL3,
+ CONTROL3_AUTO_RETRY | CONTROL3_N_RETRIES,
+ CONTROL3_AUTO_RETRY | CONTROL3_N_RETRIES),
+
+ /* set interrupts */
+ val = 0xff;
+ val &= ~(MASK_M_COLLISION | MASK_M_ALERT | MASK_M_VBUSOK);
+ regmap_write(chip->regmap, FUSB_REG_MASK, val);
+
+ val = 0xff;
+ val &= ~(MASKA_M_RETRYFAIL | MASKA_M_HARDSENT | MASKA_M_TXSENT |
+ MASKA_M_HARDRST | MASKA_M_TOGDONE);
+ regmap_write(chip->regmap, FUSB_REG_MASKA, val);
+
+ val = ~MASKB_M_GCRCSEND;
+ regmap_write(chip->regmap, FUSB_REG_MASKB, val);
+
+ tcpm_select_rp_value(chip, TYPEC_RP_1A5);
+ /* Interrupts Enable */
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL0, CONTROL0_INT_MASK,
+ ~CONTROL0_INT_MASK);
+
+ tcpm_set_vconn(chip, 0);
+
+ regmap_write(chip->regmap, FUSB_REG_POWER, 0xf);
+}
+
+static void pd_execute_hard_reset(struct fusb30x_chip *chip)
+{
+ chip->msg_id = 0;
+ chip->vdm_state = VDM_STATE_DISCOVERY_ID;
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_transition_default);
+ else
+ set_state(chip, policy_snk_transition_default);
+}
+
+static void tcpc_alert(struct fusb30x_chip *chip, u32 *evt)
+{
+ int interrupt, interrupta, interruptb;
+ u32 val;
+ static int retry;
+
+ regmap_read(chip->regmap, FUSB_REG_INTERRUPT, &interrupt);
+ regmap_read(chip->regmap, FUSB_REG_INTERRUPTA, &interrupta);
+ regmap_read(chip->regmap, FUSB_REG_INTERRUPTB, &interruptb);
+
+ if ((interrupt & INTERRUPT_COMP_CHNG) &&
+ (!(chip->cc_state & CC_STATE_TOGSS_IS_UFP))) {
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+ if (val & STATUS0_COMP)
+ *evt |= EVENT_CC;
+ }
+
+ if (interrupt & INTERRUPT_VBUSOK) {
+ if (chip->notify.is_cc_connected)
+ *evt |= EVENT_CC;
+ }
+
+ if (interrupta & INTERRUPTA_TOGDONE) {
+ *evt |= EVENT_CC;
+ regmap_read(chip->regmap, FUSB_REG_STATUS1A, &val);
+ chip->cc_state = ((u8)val >> 3) & 0x07;
+
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+ CONTROL2_TOGGLE,
+ 0);
+ }
+
+ if (interrupta & INTERRUPTA_TXSENT) {
+ *evt |= EVENT_TX;
+ chip->tx_state = tx_success;
+ }
+
+ if (interruptb & INTERRUPTB_GCRCSENT)
+ *evt |= EVENT_RX;
+
+ if (interrupta & INTERRUPTA_HARDRST) {
+ fusb302_pd_reset(chip);
+ pd_execute_hard_reset(chip);
+ *evt |= EVENT_REC_RESET;
+ }
+
+ if (interrupta & INTERRUPTA_RETRYFAIL) {
+ *evt |= EVENT_TX;
+ chip->tx_state = tx_failed;
+ }
+
+ if (interrupta & INTERRUPTA_HARDSENT) {
+ /*
+ * The fusb PD should be reset once to sync adapter PD
+ * signal after fusb sent hard reset cmd.This is not PD
+ * device if reset failed.
+ */
+ if (!retry) {
+ retry = 1;
+ fusb302_pd_reset(chip);
+ pd_execute_hard_reset(chip);
+ } else {
+ retry = 0;
+ chip->tx_state = tx_success;
+ chip->timer_state = T_DISABLED;
+ *evt |= EVENT_TX;
+ }
+ }
+}
+
+static void mux_alert(struct fusb30x_chip *chip, u32 *evt)
+{
+ if (!chip->timer_mux) {
+ *evt |= EVENT_TIMER_MUX;
+ chip->timer_mux = T_DISABLED;
+ }
+
+ if (!chip->timer_state) {
+ *evt |= EVENT_TIMER_STATE;
+ chip->timer_state = T_DISABLED;
+ }
+
+ if (chip->work_continue) {
+ *evt |= chip->work_continue;
+ chip->work_continue = 0;
+ }
+}
+
+static void set_state_unattached(struct fusb30x_chip *chip)
+{
+ dev_info(chip->dev, "connection has disconnected\n");
+ tcpm_init(chip);
+ tcpm_set_rx_enable(chip, 0);
+ set_state(chip, unattached);
+ tcpm_set_cc(chip, chip->role);
+
+ /* claer notify_info */
+ memset(&chip->notify, 0, sizeof(struct notify_info));
+ platform_fusb_notify(chip);
+
+ if (chip->gpio_discharge)
+ gpiod_set_value(chip->gpio_discharge, 1);
+ msleep(100);
+ if (chip->gpio_discharge)
+ gpiod_set_value(chip->gpio_discharge, 0);
+
+ regmap_update_bits(chip->regmap, FUSB_REG_MASK,
+ MASK_M_COMP_CHNG, MASK_M_COMP_CHNG);
+ chip->try_role_complete = false;
+}
+
+static void set_mesg(struct fusb30x_chip *chip, int cmd, int is_DMT)
+{
+ int i;
+ struct PD_CAP_INFO *pd_cap_info = &chip->pd_cap_info;
+
+ chip->send_head = ((chip->msg_id & 0x7) << 9) |
+ ((chip->notify.power_role & 0x1) << 8) |
+ (1 << 6) |
+ ((chip->notify.data_role & 0x1) << 5);
+
+ if (is_DMT) {
+ switch (cmd) {
+ case DMT_SOURCECAPABILITIES:
+ chip->send_head |= ((chip->n_caps_used & 0x3) << 12) | (cmd & 0xf);
+
+ for (i = 0; i < chip->n_caps_used; i++) {
+ chip->send_load[i] = (pd_cap_info->supply_type << 30) |
+ (pd_cap_info->dual_role_power << 29) |
+ (pd_cap_info->usb_suspend_support << 28) |
+ (pd_cap_info->externally_powered << 27) |
+ (pd_cap_info->usb_communications_cap << 26) |
+ (pd_cap_info->data_role_swap << 25) |
+ (pd_cap_info->peak_current << 20) |
+ (chip->source_power_supply[i] << 10) |
+ (chip->source_max_current[i]);
+ }
+ break;
+ case DMT_REQUEST:
+ chip->send_head |= ((1 << 12) | (cmd & 0xf));
+ /* send request with FVRDO */
+ chip->send_load[0] = (chip->pos_power << 28) |
+ (0 << 27) |
+ (1 << 26) |
+ (0 << 25) |
+ (0 << 24);
+
+ switch (CAP_POWER_TYPE(chip->rec_load[chip->pos_power - 1])) {
+ case 0:
+ /* Fixed Supply */
+ chip->send_load[0] |= ((CAP_FPDO_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 10) & 0x3ff);
+ chip->send_load[0] |= (CAP_FPDO_CURRENT(chip->rec_load[chip->pos_power - 1]) & 0x3ff);
+ break;
+ case 1:
+ /* Battery */
+ chip->send_load[0] |= ((CAP_VPDO_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 10) & 0x3ff);
+ chip->send_load[0] |= (CAP_VPDO_CURRENT(chip->rec_load[chip->pos_power - 1]) & 0x3ff);
+ break;
+ default:
+ /* not meet battery caps */
+ break;
+ }
+ break;
+ case DMT_SINKCAPABILITIES:
+ break;
+ case DMT_VENDERDEFINED:
+ break;
+ default:
+ break;
+ }
+ } else {
+ chip->send_head |= (cmd & 0xf);
+ }
+}
+
+/*
+ * This algorithm defaults to choosing higher pin config over lower ones in
+ * order to prefer multi-function if desired.
+ *
+ * NAME | SIGNALING | OUTPUT TYPE | MULTI-FUNCTION | PIN CONFIG
+ * -------------------------------------------------------------
+ * A | USB G2 | ? | no | 00_0001
+ * B | USB G2 | ? | yes | 00_0010
+ * C | DP | CONVERTED | no | 00_0100
+ * D | PD | CONVERTED | yes | 00_1000
+ * E | DP | DP | no | 01_0000
+ * F | PD | DP | yes | 10_0000
+ *
+ * if UFP has NOT asserted multi-function preferred code masks away B/D/F
+ * leaving only A/C/E. For single-output dongles that should leave only one
+ * possible pin config depending on whether its a converter DP->(VGA|HDMI) or DP
+ * output. If UFP is a USB-C receptacle it may assert C/D/E/F. The DFP USB-C
+ * receptacle must always choose C/D in those cases.
+ */
+static int pd_dfp_dp_get_pin_assignment(struct fusb30x_chip *chip,
+ uint32_t caps, uint32_t status)
+{
+ uint32_t pin_caps;
+
+ /* revisit with DFP that can be a sink */
+ pin_caps = PD_DP_PIN_CAPS(caps);
+
+ /* if don't want multi-function then ignore those pin configs */
+ if (!PD_VDO_DPSTS_MF_PREF(status))
+ pin_caps &= ~MODE_DP_PIN_MF_MASK;
+
+ /* revisit if DFP drives USB Gen 2 signals */
+ if (PD_DP_SIGNAL_GEN2(caps))
+ pin_caps &= ~MODE_DP_PIN_DP_MASK;
+ else
+ pin_caps &= ~MODE_DP_PIN_BR2_MASK;
+
+ /* if C/D present they have precedence over E/F for USB-C->USB-C */
+ if (pin_caps & (MODE_DP_PIN_C | MODE_DP_PIN_D))
+ pin_caps &= ~(MODE_DP_PIN_E | MODE_DP_PIN_F);
+
+ /* returns undefined for zero */
+ if (!pin_caps)
+ return 0;
+
+ /* choosing higher pin config over lower ones */
+ return 1 << (31 - __builtin_clz(pin_caps));
+}
+
+static void set_vdm_mesg(struct fusb30x_chip *chip, int cmd, int type, int mode)
+{
+ chip->send_head = (chip->msg_id & 0x7) << 9;
+ chip->send_head |= (chip->notify.power_role & 0x1) << 8;
+
+ chip->send_head = ((chip->msg_id & 0x7) << 9) |
+ ((chip->notify.power_role & 0x1) << 8) |
+ (1 << 6) |
+ ((chip->notify.data_role & 0x1) << 5) |
+ (DMT_VENDERDEFINED & 0xf);
+
+ chip->send_load[0] = (1 << 15) |
+ (0 << 13) |
+ (type << 6) |
+ (cmd);
+
+ switch (cmd) {
+ case VDM_DISCOVERY_ID:
+ case VDM_DISCOVERY_SVIDS:
+ case VDM_ATTENTION:
+ chip->send_load[0] |= (0xff00 << 16);
+ chip->send_head |= (1 << 12);
+ break;
+ case VDM_DISCOVERY_MODES:
+ chip->send_load[0] |=
+ (chip->vdm_svid[chip->val_tmp >> 1] << 16);
+ chip->send_head |= (1 << 12);
+ break;
+ case VDM_ENTER_MODE:
+ chip->send_head |= (1 << 12);
+ chip->send_load[0] |= (mode << 8) | (0xff01 << 16);
+ break;
+ case VDM_EXIT_MODE:
+ chip->send_head |= (1 << 12);
+ chip->send_load[0] |= (0x0f << 8) | (0xff01 << 16);
+ break;
+ case VDM_DP_STATUS_UPDATE:
+ chip->send_head |= (2 << 12);
+ chip->send_load[0] |= (1 << 8) | (0xff01 << 16);
+ chip->send_load[1] = 5;
+ break;
+ case VDM_DP_CONFIG:
+ chip->send_head |= (2 << 12);
+ chip->send_load[0] |= (1 << 8) | (0xff01 << 16);
+
+ chip->notify.pin_assignment_def =
+ pd_dfp_dp_get_pin_assignment(chip, chip->notify.dp_caps,
+ chip->notify.dp_status);
+
+ chip->send_load[1] = (chip->notify.pin_assignment_def << 8) |
+ (1 << 2) | 2;
+ dev_dbg(chip->dev, "DisplayPort Configurations: 0x%08x\n",
+ chip->send_load[1]);
+ break;
+ default:
+ break;
+ }
+}
+
+static enum tx_state policy_send_hardrst(struct fusb30x_chip *chip, u32 evt)
+{
+ switch (chip->tx_state) {
+ case 0:
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL3,
+ CONTROL3_SEND_HARDRESET,
+ CONTROL3_SEND_HARDRESET);
+ chip->tx_state = tx_busy;
+ chip->timer_state = T_BMC_TIMEOUT;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ break;
+ default:
+ if (evt & EVENT_TIMER_STATE)
+ chip->tx_state = tx_success;
+ break;
+ }
+ return chip->tx_state;
+}
+
+static enum tx_state policy_send_data(struct fusb30x_chip *chip)
+{
+ u8 senddata[40];
+ int pos = 0;
+ u8 len;
+
+ switch (chip->tx_state) {
+ case 0:
+ senddata[pos++] = FUSB_TKN_SYNC1;
+ senddata[pos++] = FUSB_TKN_SYNC1;
+ senddata[pos++] = FUSB_TKN_SYNC1;
+ senddata[pos++] = FUSB_TKN_SYNC2;
+
+ len = PD_HEADER_CNT(chip->send_head) << 2;
+ senddata[pos++] = FUSB_TKN_PACKSYM | ((len + 2) & 0x1f);
+
+ senddata[pos++] = chip->send_head & 0xff;
+ senddata[pos++] = (chip->send_head >> 8) & 0xff;
+
+ memcpy(&senddata[pos], chip->send_load, len);
+ pos += len;
+
+ senddata[pos++] = FUSB_TKN_JAMCRC;
+ senddata[pos++] = FUSB_TKN_EOP;
+ senddata[pos++] = FUSB_TKN_TXOFF;
+ senddata[pos++] = FUSB_TKN_TXON;
+
+ regmap_raw_write(chip->regmap, FUSB_REG_FIFO, senddata, pos);
+ chip->tx_state = tx_busy;
+ break;
+
+ default:
+ /* wait Tx result */
+ break;
+ }
+
+ return chip->tx_state;
+}
+
+static void process_vdm_msg(struct fusb30x_chip *chip)
+{
+ u32 vdm_header = chip->rec_load[0];
+ int i;
+ u32 tmp;
+
+ /* can't procee unstructed vdm msg */
+ if (!GET_VDMHEAD_STRUCT_TYPE(vdm_header)) {
+ dev_warn(chip->dev, "unknown unstructed vdm message\n");
+ return;
+ }
+
+ switch (GET_VDMHEAD_CMD_TYPE(vdm_header)) {
+ case VDM_TYPE_INIT:
+ switch (GET_VDMHEAD_CMD(vdm_header)) {
+ case VDM_ATTENTION:
+ chip->notify.dp_status = GET_DP_STATUS(chip->rec_load[1]);
+ dev_info(chip->dev, "attention, dp_status %x\n",
+ chip->rec_load[1]);
+ chip->notify.attention = true;
+ platform_fusb_notify(chip);
+ break;
+ default:
+ dev_warn(chip->dev, "rec unknown init vdm msg\n");
+ break;
+ }
+ break;
+ case VDM_TYPE_ACK:
+ switch (GET_VDMHEAD_CMD(vdm_header)) {
+ case VDM_DISCOVERY_ID:
+ chip->vdm_id = chip->rec_load[1];
+ break;
+ case VDM_DISCOVERY_SVIDS:
+ for (i = 0; i < 6; i++) {
+ tmp = (chip->rec_load[i + 1] >> 16) &
+ 0x0000ffff;
+ if (tmp) {
+ chip->vdm_svid[i * 2] = tmp;
+ chip->vdm_svid_num++;
+ } else {
+ break;
+ }
+
+ tmp = (chip->rec_load[i + 1] & 0x0000ffff);
+ if (tmp) {
+ chip->vdm_svid[i * 2 + 1] = tmp;
+ chip->vdm_svid_num++;
+ } else {
+ break;
+ }
+ }
+ break;
+ case VDM_DISCOVERY_MODES:
+ /* indicate there are some vdo modes */
+ if (PD_HEADER_CNT(chip->rec_head) > 1) {
+ /*
+ * store mode config,
+ * enter first mode default
+ */
+ tmp = chip->rec_load[1];
+
+ if ((!((tmp >> 8) & 0x3f)) &&
+ (!((tmp >> 16) & 0x3f))) {
+ chip->val_tmp |= 1;
+ break;
+ }
+ chip->notify.dp_caps = chip->rec_load[1];
+ chip->notify.pin_assignment_def = 0;
+ chip->notify.pin_assignment_support =
+ PD_DP_PIN_CAPS(tmp);
+ chip->val_tmp |= 1;
+ dev_dbg(chip->dev,
+ "DisplayPort Capabilities: 0x%08x\n",
+ chip->rec_load[1]);
+ }
+ break;
+ case VDM_ENTER_MODE:
+ chip->val_tmp = 1;
+ break;
+ case VDM_DP_STATUS_UPDATE:
+ chip->notify.dp_status = GET_DP_STATUS(chip->rec_load[1]);
+ dev_dbg(chip->dev, "DisplayPort Status: 0x%08x\n",
+ chip->rec_load[1]);
+ chip->val_tmp = 1;
+ break;
+ case VDM_DP_CONFIG:
+ chip->val_tmp = 1;
+ dev_info(chip->dev,
+ "DP config successful, pin_assignment 0x%x\n",
+ chip->notify.pin_assignment_def);
+ chip->notify.is_enter_mode = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ case VDM_TYPE_NACK:
+ dev_warn(chip->dev, "REC NACK for 0x%x\n",
+ GET_VDMHEAD_CMD(vdm_header));
+ /* disable vdm */
+ chip->vdm_state = VDM_STATE_ERR;
+ break;
+ }
+}
+
+static int vdm_send_discoveryid(struct fusb30x_chip *chip, u32 evt)
+{
+ int tmp;
+
+ switch (chip->vdm_send_state) {
+ case 0:
+ set_vdm_mesg(chip, VDM_DISCOVERY_ID, VDM_TYPE_INIT, 0);
+ chip->vdm_id = 0;
+ chip->tx_state = 0;
+ chip->vdm_send_state++;
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->vdm_send_state++;
+ chip->timer_state = T_SENDER_RESPONSE;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (tmp == tx_failed) {
+ dev_warn(chip->dev, "VDM_DISCOVERY_ID send failed\n");
+ /* disable auto_vdm_machine */
+ chip->vdm_state = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->vdm_id) {
+ chip->vdm_send_state = 0;
+ return 0;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "VDM_DISCOVERY_ID time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ return -EINPROGRESS;
+}
+
+static int vdm_send_discoverysvid(struct fusb30x_chip *chip, u32 evt)
+{
+ int tmp;
+
+ switch (chip->vdm_send_state) {
+ case 0:
+ set_vdm_mesg(chip, VDM_DISCOVERY_SVIDS, VDM_TYPE_INIT, 0);
+ memset(chip->vdm_svid, 0, sizeof(chip->vdm_svid));
+ chip->vdm_svid_num = 0;
+ chip->tx_state = 0;
+ chip->vdm_send_state++;
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->vdm_send_state++;
+ chip->timer_state = T_SENDER_RESPONSE;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (tmp == tx_failed) {
+ dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS send failed\n");
+ /* disable auto_vdm_machine */
+ chip->vdm_state = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->vdm_svid_num) {
+ chip->vdm_send_state = 0;
+ return 0;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ return -EINPROGRESS;
+}
+
+static int vdm_send_discoverymodes(struct fusb30x_chip *chip, u32 evt)
+{
+ int tmp;
+
+ if ((chip->val_tmp >> 1) != chip->vdm_svid_num) {
+ switch (chip->vdm_send_state) {
+ case 0:
+ set_vdm_mesg(chip, VDM_DISCOVERY_MODES,
+ VDM_TYPE_INIT, 0);
+ chip->tx_state = 0;
+ chip->vdm_send_state++;
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->vdm_send_state++;
+ chip->timer_state = T_SENDER_RESPONSE;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (tmp == tx_failed) {
+ dev_warn(chip->dev,
+ "VDM_DISCOVERY_MODES send failed\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->val_tmp & 1) {
+ chip->val_tmp &= 0xfe;
+ chip->val_tmp += 2;
+ chip->vdm_send_state = 0;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev,
+ "VDM_DISCOVERY_MODES time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ } else {
+ chip->val_tmp = 0;
+ return 0;
+ }
+
+ return -EINPROGRESS;
+}
+
+static int vdm_send_entermode(struct fusb30x_chip *chip, u32 evt)
+{
+ int tmp;
+
+ switch (chip->vdm_send_state) {
+ case 0:
+ set_vdm_mesg(chip, VDM_ENTER_MODE, VDM_TYPE_INIT, 1);
+ chip->tx_state = 0;
+ chip->vdm_send_state++;
+ chip->notify.is_enter_mode = false;
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->vdm_send_state++;
+ chip->timer_state = T_SENDER_RESPONSE;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (tmp == tx_failed) {
+ dev_warn(chip->dev, "VDM_ENTER_MODE send failed\n");
+ /* disable auto_vdm_machine */
+ chip->vdm_state = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->val_tmp) {
+ chip->val_tmp = 0;
+ chip->vdm_send_state = 0;
+ return 0;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "VDM_ENTER_MODE time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ return -EINPROGRESS;
+}
+
+static int vdm_send_getdpstatus(struct fusb30x_chip *chip, u32 evt)
+{
+ int tmp;
+
+ switch (chip->vdm_send_state) {
+ case 0:
+ set_vdm_mesg(chip, VDM_DP_STATUS_UPDATE, VDM_TYPE_INIT, 1);
+ chip->tx_state = 0;
+ chip->vdm_send_state++;
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->vdm_send_state++;
+ chip->timer_state = T_SENDER_RESPONSE;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (tmp == tx_failed) {
+ dev_warn(chip->dev,
+ "VDM_DP_STATUS_UPDATE send failed\n");
+ /* disable auto_vdm_machine */
+ chip->vdm_state = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->val_tmp) {
+ chip->val_tmp = 0;
+ chip->vdm_send_state = 0;
+ return 0;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "VDM_DP_STATUS_UPDATE time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ return -EINPROGRESS;
+}
+
+static int vdm_send_dpconfig(struct fusb30x_chip *chip, u32 evt)
+{
+ int tmp;
+
+ switch (chip->vdm_send_state) {
+ case 0:
+ set_vdm_mesg(chip, VDM_DP_CONFIG, VDM_TYPE_INIT, 0);
+ chip->tx_state = 0;
+ chip->vdm_send_state++;
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->vdm_send_state++;
+ chip->timer_state = T_SENDER_RESPONSE;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (tmp == tx_failed) {
+ dev_warn(chip->dev, "vdm_send_dpconfig send failed\n");
+ /* disable auto_vdm_machine */
+ chip->vdm_state = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->val_tmp) {
+ chip->val_tmp = 0;
+ chip->vdm_send_state = 0;
+ return 0;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "vdm_send_dpconfig time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ return -EINPROGRESS;
+}
+
+/* without break if success */
+#define AUTO_VDM_HANDLE(func, chip, evt, conditions) \
+do { \
+ conditions = func(chip, evt); \
+ if (!conditions) { \
+ chip->vdm_state++; \
+ chip->work_continue |= EVENT_WORK_CONTINUE; \
+ } else { \
+ if (conditions != -EINPROGRESS) \
+ chip->vdm_state = VDM_STATE_ERR; \
+ } \
+} while (0)
+
+static void auto_vdm_machine(struct fusb30x_chip *chip, u32 evt)
+{
+ int conditions;
+
+ switch (chip->vdm_state) {
+ case VDM_STATE_DISCOVERY_ID:
+ AUTO_VDM_HANDLE(vdm_send_discoveryid, chip, evt, conditions);
+ break;
+ case VDM_STATE_DISCOVERY_SVID:
+ AUTO_VDM_HANDLE(vdm_send_discoverysvid, chip, evt, conditions);
+ break;
+ case VDM_STATE_DISCOVERY_MODES:
+ AUTO_VDM_HANDLE(vdm_send_discoverymodes, chip, evt, conditions);
+ break;
+ case VDM_STATE_ENTER_MODE:
+ AUTO_VDM_HANDLE(vdm_send_entermode, chip, evt, conditions);
+ break;
+ case VDM_STATE_UPDATE_STATUS:
+ AUTO_VDM_HANDLE(vdm_send_getdpstatus, chip, evt, conditions);
+ break;
+ case VDM_STATE_DP_CONFIG:
+ AUTO_VDM_HANDLE(vdm_send_dpconfig, chip, evt, conditions);
+ break;
+ case VDM_STATE_NOTIFY:
+ platform_fusb_notify(chip);
+ chip->vdm_state = VDM_STATE_READY;
+ break;
+ default:
+ break;
+ }
+}
+
+static void fusb_state_disabled(struct fusb30x_chip *chip, u32 evt)
+{
+ /* Do nothing */
+}
+
+static void fusb_state_unattached(struct fusb30x_chip *chip, u32 evt)
+{
+ chip->notify.is_cc_connected = false;
+ chip->is_pd_support = false;
+
+ if ((evt & EVENT_CC) && chip->cc_state) {
+ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP)
+ set_state(chip, attach_wait_sink);
+ else
+ set_state(chip, attach_wait_source);
+
+ chip->vbus_begin = tcpm_check_vbus(chip);
+
+ tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ?
+ TYPEC_POLARITY_CC1 :
+ TYPEC_POLARITY_CC2);
+ tcpm_get_cc(chip, &chip->cc1, &chip->cc2);
+ chip->debounce_cnt = 0;
+ chip->timer_mux = 2;
+ fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux);
+ }
+}
+
+static void fusb_state_try_attach_set(struct fusb30x_chip *chip,
+ enum role_mode mode)
+{
+ if (mode == ROLE_MODE_NONE || mode == ROLE_MODE_DRP ||
+ mode == ROLE_MODE_ASS)
+ return;
+
+ tcpm_init(chip);
+ tcpm_set_cc(chip, (mode == ROLE_MODE_DFP) ?
+ ROLE_MODE_DFP : ROLE_MODE_UFP);
+ chip->timer_mux = T_PD_TRY_DRP;
+ fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux);
+ set_state(chip, (mode == ROLE_MODE_DFP) ?
+ attach_try_src : attach_try_snk);
+}
+
+static void fusb_state_attach_wait_sink(struct fusb30x_chip *chip, u32 evt)
+{
+ int cc1, cc2;
+
+ if (evt & EVENT_TIMER_MUX) {
+ if (tcpm_check_vbus(chip)) {
+ chip->timer_mux = T_DISABLED;
+ if (chip->role == ROLE_MODE_DRP &&
+ chip->try_role == ROLE_MODE_DFP &&
+ !chip->try_role_complete) {
+ fusb_state_try_attach_set(chip, ROLE_MODE_DFP);
+ return;
+ } else if (chip->try_role_complete) {
+ chip->timer_mux = T_PD_SOURCE_ON;
+ fusb_timer_start(&chip->timer_mux_machine,
+ chip->timer_mux);
+ set_state(chip, attached_sink);
+ return;
+ }
+ }
+
+ tcpm_get_cc(chip, &cc1, &cc2);
+
+ if ((chip->cc1 == cc1) && (chip->cc2 == cc2)) {
+ chip->debounce_cnt++;
+ } else {
+ chip->cc1 = cc1;
+ chip->cc2 = cc2;
+ chip->debounce_cnt = 0;
+ }
+
+ if (chip->debounce_cnt > N_DEBOUNCE_CNT) {
+ chip->timer_mux = T_DISABLED;
+ if ((chip->cc1 == TYPEC_CC_VOLT_RP &&
+ chip->cc2 == TYPEC_CC_VOLT_OPEN) ||
+ (chip->cc2 == TYPEC_CC_VOLT_RP &&
+ chip->cc1 == TYPEC_CC_VOLT_OPEN)) {
+ chip->timer_mux = T_PD_SOURCE_ON;
+ fusb_timer_start(&chip->timer_mux_machine,
+ chip->timer_mux);
+ set_state(chip, attached_sink);
+ } else {
+ set_state_unattached(chip);
+ }
+ return;
+ }
+
+ chip->timer_mux = 2;
+ fusb_timer_start(&chip->timer_mux_machine,
+ chip->timer_mux);
+ }
+}
+
+static void fusb_state_attach_wait_source(struct fusb30x_chip *chip, u32 evt)
+{
+ int cc1, cc2;
+
+ if (evt & EVENT_TIMER_MUX) {
+ tcpm_get_cc(chip, &cc1, &cc2);
+
+ if ((chip->cc1 == cc1) && (chip->cc2 == cc2)) {
+ chip->debounce_cnt++;
+ } else {
+ chip->cc1 = cc1;
+ chip->cc2 = cc2;
+ chip->debounce_cnt = 0;
+ }
+
+ if (chip->debounce_cnt > N_DEBOUNCE_CNT) {
+ if (((!chip->cc1) || (!chip->cc2)) &&
+ ((chip->cc1 == TYPEC_CC_VOLT_RD) ||
+ (chip->cc2 == TYPEC_CC_VOLT_RD))) {
+ if (chip->role == ROLE_MODE_DRP &&
+ chip->try_role == ROLE_MODE_UFP &&
+ !chip->try_role_complete)
+ fusb_state_try_attach_set(chip,
+ ROLE_MODE_UFP);
+ else
+ set_state(chip, attached_source);
+ } else {
+ set_state_unattached(chip);
+ }
+ return;
+ }
+
+ chip->timer_mux = 2;
+ fusb_timer_start(&chip->timer_mux_machine,
+ chip->timer_mux);
+ }
+}
+
+static void fusb_state_attached_source(struct fusb30x_chip *chip, u32 evt)
+{
+ platform_set_vbus_lvl_enable(chip, 1, 0);
+ tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ?
+ TYPEC_POLARITY_CC1 : TYPEC_POLARITY_CC2);
+ tcpm_set_vconn(chip, 1);
+
+ chip->notify.is_cc_connected = true;
+
+ chip->notify.power_role = POWER_ROLE_SOURCE;
+ chip->notify.data_role = DATA_ROLE_DFP;
+ chip->hardrst_count = 0;
+ set_state(chip, policy_src_startup);
+ regmap_update_bits(chip->regmap, FUSB_REG_MASK, MASK_M_COMP_CHNG, 0);
+ dev_info(chip->dev, "CC connected in %s as DFP\n",
+ chip->cc_polarity ? "CC1" : "CC2");
+}
+
+static void fusb_state_attached_sink(struct fusb30x_chip *chip, u32 evt)
+{
+ if (tcpm_check_vbus(chip)) {
+ chip->timer_mux = T_DISABLED;
+ chip->timer_state = T_DISABLED;
+ if (!chip->try_role_complete &&
+ chip->try_role == ROLE_MODE_DFP &&
+ chip->role == ROLE_MODE_DRP) {
+ fusb_state_try_attach_set(chip, ROLE_MODE_DFP);
+ return;
+ }
+
+ chip->try_role_complete = true;
+ chip->notify.is_cc_connected = true;
+ chip->notify.power_role = POWER_ROLE_SINK;
+ chip->notify.data_role = DATA_ROLE_UFP;
+ chip->hardrst_count = 0;
+ set_state(chip, policy_snk_startup);
+ dev_info(chip->dev, "CC connected in %s as UFP\n",
+ chip->cc_polarity ? "CC1" : "CC2");
+ return;
+ } else if (evt & EVENT_TIMER_MUX) {
+ set_state_unattached(chip);
+ return;
+ }
+
+ chip->timer_state = 2;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+}
+
+static void fusb_state_try_attach(struct fusb30x_chip *chip, u32 evt,
+ enum role_mode mode)
+{
+ if ((evt & EVENT_CC) && chip->cc_state) {
+ chip->try_role_complete = true;
+ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP)
+ set_state(chip, (mode == ROLE_MODE_UFP) ?
+ attach_wait_sink : error_recovery);
+ else
+ set_state(chip, (mode == ROLE_MODE_DFP) ?
+ attach_wait_source : error_recovery);
+
+ tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ?
+ TYPEC_POLARITY_CC1 :
+ TYPEC_POLARITY_CC2);
+ tcpm_get_cc(chip, &chip->cc1, &chip->cc2);
+ chip->debounce_cnt = 0;
+ chip->timer_mux = 2;
+ fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux);
+ } else if (evt & EVENT_TIMER_MUX) {
+ if (!chip->try_role_complete) {
+ chip->try_role_complete = true;
+ fusb_state_try_attach_set(chip,
+ (mode == ROLE_MODE_DFP) ?
+ ROLE_MODE_UFP :
+ ROLE_MODE_DFP);
+ } else {
+ set_state(chip, error_recovery);
+ }
+ }
+}
+
+static void fusb_soft_reset_parameter(struct fusb30x_chip *chip)
+{
+ chip->caps_counter = 0;
+ chip->msg_id = 0;
+ chip->vdm_state = VDM_STATE_DISCOVERY_ID;
+ chip->vdm_substate = 0;
+ chip->vdm_send_state = 0;
+ chip->val_tmp = 0;
+ chip->pos_power = 0;
+}
+
+static void fusb_state_src_startup(struct fusb30x_chip *chip, u32 evt)
+{
+ chip->notify.is_pd_connected = false;
+ fusb_soft_reset_parameter(chip);
+
+ memset(chip->partner_cap, 0, sizeof(chip->partner_cap));
+
+ tcpm_set_msg_header(chip);
+ tcpm_set_polarity(chip, chip->cc_polarity);
+ tcpm_set_rx_enable(chip, 1);
+
+ set_state(chip, policy_src_send_caps);
+ platform_fusb_notify(chip);
+}
+
+static void fusb_state_src_discovery(struct fusb30x_chip *chip, u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->caps_counter++;
+
+ if (chip->caps_counter < N_CAPS_COUNT) {
+ chip->timer_state = T_TYPEC_SEND_SOURCECAP;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state = 1;
+ } else {
+ set_state(chip, disabled);
+ }
+ break;
+ default:
+ if (evt & EVENT_TIMER_STATE) {
+ set_state(chip, policy_src_send_caps);
+ } else if (evt & EVENT_TIMER_MUX) {
+ if (!chip->is_pd_support)
+ set_state(chip, disabled);
+ else if (chip->hardrst_count > N_HARDRESET_COUNT)
+ set_state(chip, error_recovery);
+ else
+ set_state(chip, policy_src_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_src_send_caps(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, DMT_SOURCECAPABILITIES, DATAMESSAGE);
+ chip->sub_state = 1;
+ chip->tx_state = tx_idle;
+ /* without break */
+ case 1:
+ tmp = policy_send_data(chip);
+
+ if (tmp == tx_success) {
+ chip->hardrst_count = 0;
+ chip->caps_counter = 0;
+ chip->timer_state = T_SENDER_RESPONSE;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->timer_mux = T_DISABLED;
+ chip->sub_state++;
+ chip->is_pd_support = true;
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_src_discovery);
+ break;
+ }
+
+ if (!(evt & FLAG_EVENT))
+ break;
+ default:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_REQUEST)) {
+ set_state(chip, policy_src_negotiate_cap);
+ } else {
+ set_state(chip, policy_src_send_softrst);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ if (chip->hardrst_count <= N_HARDRESET_COUNT)
+ set_state(chip, policy_src_send_hardrst);
+ else
+ set_state(chip, disabled);
+ } else if (evt & EVENT_TIMER_MUX) {
+ if (!chip->is_pd_support)
+ set_state(chip, disabled);
+ else if (chip->hardrst_count > N_HARDRESET_COUNT)
+ set_state(chip, error_recovery);
+ else
+ set_state(chip, policy_src_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_src_negotiate_cap(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ /* base on evb1 */
+ tmp = (chip->rec_load[0] >> 28) & 0x07;
+ if (tmp > chip->n_caps_used)
+ set_state(chip, policy_src_cap_response);
+ else
+ set_state(chip, policy_src_transition_supply);
+}
+
+static void fusb_state_src_transition_supply(struct fusb30x_chip *chip,
+ u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* without break */
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->timer_state = T_SRC_TRANSITION;
+ chip->sub_state++;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_src_send_softrst);
+ }
+ break;
+ case 2:
+ if (evt & EVENT_TIMER_STATE) {
+ chip->notify.is_pd_connected = true;
+ platform_set_vbus_lvl_enable(chip, 1, 0);
+ set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ }
+ break;
+ default:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ dev_info(chip->dev,
+ "PD connected as DFP, supporting 5V\n");
+ set_state(chip, policy_src_ready);
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_src_send_softrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_src_cap_response(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_REJECT, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* without break */
+ default:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ if (chip->notify.is_pd_connected) {
+ dev_info(chip->dev,
+ "PD connected as DFP, supporting 5V\n");
+ set_state(chip, policy_src_ready);
+ } else {
+ set_state(chip, policy_src_send_hardrst);
+ }
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_src_send_softrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_src_transition_default(struct fusb30x_chip *chip,
+ u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->notify.is_pd_connected = false;
+ platform_set_vbus_lvl_enable(chip, 0, 0);
+ if (chip->notify.data_role)
+ regmap_update_bits(chip->regmap,
+ FUSB_REG_SWITCHES1,
+ SWITCHES1_DATAROLE,
+ SWITCHES1_DATAROLE);
+ else
+ regmap_update_bits(chip->regmap,
+ FUSB_REG_SWITCHES1,
+ SWITCHES1_DATAROLE,
+ 0);
+
+ chip->timer_state = T_SRC_RECOVER;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ break;
+ default:
+ if (evt & EVENT_TIMER_STATE) {
+ platform_set_vbus_lvl_enable(chip, 1, 0);
+ chip->timer_mux = T_NO_RESPONSE;
+ fusb_timer_start(&chip->timer_mux_machine,
+ chip->timer_mux);
+ set_state(chip, policy_src_startup);
+ dev_dbg(chip->dev, "reset over-> src startup\n");
+ }
+ break;
+ }
+}
+
+static void fusb_state_vcs_ufp_evaluate_swap(struct fusb30x_chip *chip, u32 evt)
+{
+ if (chip->vconn_supported)
+ set_state(chip, policy_vcs_ufp_accept);
+ else
+ set_state(chip, policy_vcs_ufp_reject);
+}
+
+static void fusb_state_swap_msg_process(struct fusb30x_chip *chip, u32 evt)
+{
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PR_SWAP)) {
+ set_state(chip, policy_src_prs_evaluate);
+ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_VCONN_SWAP)) {
+ if (chip->notify.data_role)
+ set_state(chip, chip->conn_state);
+ else
+ set_state(chip, policy_vcs_ufp_evaluate_swap);
+ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_DR_SWAP)) {
+ if (chip->notify.data_role)
+ set_state(chip, policy_drs_dfp_evaluate);
+ else
+ set_state(chip, policy_drs_ufp_evaluate);
+ }
+ }
+}
+
+#define VDM_IS_ACTIVE(chip) \
+ (chip->notify.data_role && chip->vdm_state < VDM_STATE_READY)
+
+static void fusb_state_src_ready(struct fusb30x_chip *chip, u32 evt)
+{
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_VENDERDEFINED)) {
+ process_vdm_msg(chip);
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ chip->timer_state = T_DISABLED;
+ } else if (!VDM_IS_ACTIVE(chip)) {
+ fusb_state_swap_msg_process(chip, evt);
+ }
+ }
+
+ if (!chip->partner_cap[0])
+ set_state(chip, policy_src_get_sink_caps);
+ else if (VDM_IS_ACTIVE(chip))
+ auto_vdm_machine(chip, evt);
+}
+
+static void fusb_state_prs_evaluate(struct fusb30x_chip *chip, u32 evt)
+{
+ if (chip->role == ROLE_MODE_DRP)
+ set_state(chip, policy_src_prs_accept);
+ else
+ set_state(chip, policy_src_prs_reject);
+}
+
+static void fusb_state_send_simple_msg(struct fusb30x_chip *chip, u32 evt,
+ int cmd, int is_DMT,
+ enum connection_state state_success,
+ enum connection_state state_failed)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, cmd, is_DMT);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success)
+ set_state(chip, state_success);
+ else if (tmp == tx_failed)
+ set_state(chip, state_failed);
+ }
+}
+
+static void fusb_state_prs_reject(struct fusb30x_chip *chip, u32 evt)
+{
+ fusb_state_send_simple_msg(chip, evt, CMT_REJECT, CONTROLMESSAGE,
+ (chip->notify.power_role) ?
+ policy_src_ready : policy_snk_ready,
+ (chip->notify.power_role) ?
+ policy_src_send_softrst :
+ policy_snk_send_softrst);
+}
+
+static void fusb_state_prs_accept(struct fusb30x_chip *chip, u32 evt)
+{
+ fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE,
+ (chip->notify.power_role) ?
+ policy_src_prs_transition_to_off :
+ policy_snk_prs_transition_to_off,
+ (chip->notify.power_role) ?
+ policy_src_send_softrst :
+ policy_snk_send_softrst);
+}
+
+static void fusb_state_vcs_ufp_accept(struct fusb30x_chip *chip, u32 evt)
+{
+ fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE,
+ (chip->vconn_enabled) ?
+ policy_vcs_ufp_wait_for_dfp_vconn :
+ policy_vcs_ufp_turn_on_vconn,
+ (chip->notify.power_role) ?
+ policy_src_send_softrst :
+ policy_snk_send_softrst);
+}
+
+static void fusb_state_vcs_set_vconn(struct fusb30x_chip *chip,
+ u32 evt, bool on)
+{
+ if (on) {
+ tcpm_set_vconn(chip, 1);
+ set_state(chip, chip->notify.data_role ?
+ policy_vcs_dfp_send_ps_rdy :
+ policy_vcs_ufp_send_ps_rdy);
+ } else {
+ tcpm_set_vconn(chip, 0);
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_ready);
+ else
+ set_state(chip, policy_snk_ready);
+ }
+}
+
+static void fusb_state_vcs_send_ps_rdy(struct fusb30x_chip *chip, u32 evt)
+{
+ fusb_state_send_simple_msg(chip, evt, CMT_PS_RDY, CONTROLMESSAGE,
+ (chip->notify.power_role) ?
+ policy_src_ready : policy_snk_ready,
+ (chip->notify.power_role) ?
+ policy_src_send_softrst :
+ policy_snk_send_softrst);
+}
+
+static void fusb_state_vcs_wait_for_vconn(struct fusb30x_chip *chip,
+ u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->timer_state = T_PD_VCONN_SRC_ON;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PS_RDY))
+ set_state(chip, chip->notify.data_role ?
+ policy_vcs_dfp_turn_off_vconn :
+ policy_vcs_ufp_turn_off_vconn);
+ } else if (evt & EVENT_TIMER_STATE) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_send_hardrst);
+ else
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ }
+}
+
+static void fusb_state_src_prs_transition_to_off(struct fusb30x_chip *chip,
+ u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->timer_state = T_SRC_TRANSITION;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ break;
+ case 1:
+ if (evt & EVENT_TIMER_STATE) {
+ platform_set_vbus_lvl_enable(chip, 0, 0);
+ chip->notify.power_role = POWER_ROLE_SINK;
+ tcpm_set_msg_header(chip);
+ if (chip->role == ROLE_MODE_DRP)
+ set_state(chip, policy_src_prs_assert_rd);
+ else
+ set_state(chip, policy_src_prs_source_off);
+ }
+ }
+}
+
+static void fusb_state_src_prs_assert_rd(struct fusb30x_chip *chip, u32 evt)
+{
+ tcpm_set_cc_pull_mode(chip, CC_PULL_DOWN);
+ set_state(chip, policy_src_prs_source_off);
+}
+
+static void fusb_state_src_prs_source_off(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->timer_state = T_PD_SOURCE_ON;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ } else if (tmp == tx_failed) {
+ chip->notify.power_role = POWER_ROLE_SOURCE;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_src_send_hardrst);
+ }
+ if (chip->sub_state != 3)
+ break;
+ case 2:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_PS_RDY)) {
+ chip->timer_state = T_DISABLED;
+ /* snk startup */
+ chip->notify.is_pd_connected = false;
+ chip->cc_state |= CC_STATE_TOGSS_IS_UFP;
+ tcpm_set_polarity(chip, chip->cc_polarity);
+ tcpm_set_rx_enable(chip, 1);
+ set_state(chip, policy_snk_discovery);
+ } else {
+ dev_dbg(chip->dev,
+ "rec careless msg: head %x\n",
+ chip->rec_head);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ chip->notify.power_role = POWER_ROLE_SOURCE;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_src_send_hardrst);
+ }
+ }
+}
+
+static void fusb_state_drs_evaluate(struct fusb30x_chip *chip, u32 evt)
+{
+ if (chip->pd_cap_info.data_role_swap)
+ /*
+ * TODO:
+ * NOW REJECT swap when the port is DFP
+ * since we should work together with USB part
+ */
+ set_state(chip, chip->notify.data_role ?
+ policy_drs_dfp_reject : policy_drs_ufp_accept);
+ else
+ set_state(chip, chip->notify.data_role ?
+ policy_drs_dfp_reject : policy_drs_ufp_reject);
+}
+
+static void fusb_state_drs_send_accept(struct fusb30x_chip *chip, u32 evt)
+{
+ fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE,
+ chip->notify.power_role ?
+ policy_drs_dfp_change :
+ policy_drs_ufp_change,
+ error_recovery);
+}
+
+static void fusb_state_drs_role_change(struct fusb30x_chip *chip, u32 evt)
+{
+ chip->notify.data_role = chip->notify.data_role ?
+ DATA_ROLE_UFP : DATA_ROLE_DFP;
+ tcpm_set_msg_header(chip);
+ set_state(chip, chip->notify.power_role ? policy_src_ready :
+ policy_snk_ready);
+}
+
+static void fusb_state_src_get_sink_cap(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_GETSINKCAP, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* without break */
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->timer_state = T_SENDER_RESPONSE;
+ chip->sub_state++;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_src_send_softrst);
+ }
+
+ if (!(evt & FLAG_EVENT))
+ break;
+ default:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_DATA_MSG(chip->rec_head,
+ DMT_SINKCAPABILITIES)) {
+ for (tmp = 0;
+ tmp < PD_HEADER_CNT(chip->rec_head);
+ tmp++) {
+ chip->partner_cap[tmp] =
+ chip->rec_load[tmp];
+ }
+ set_state(chip, policy_src_ready);
+ } else {
+ chip->partner_cap[0] = 0xffffffff;
+ set_state(chip, policy_src_ready);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "Get sink cap time out\n");
+ chip->partner_cap[0] = 0xffffffff;
+ set_state(chip, policy_src_ready);
+ }
+ }
+}
+
+static void fusb_state_src_send_hardreset(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* without break */
+ default:
+ tmp = policy_send_hardrst(chip, evt);
+ if (tmp == tx_success) {
+ chip->hardrst_count++;
+ set_state(chip, policy_src_transition_default);
+ } else if (tmp == tx_failed) {
+ /* can't reach here */
+ set_state(chip, error_recovery);
+ }
+ break;
+ }
+}
+
+static void fusb_state_src_softreset(struct fusb30x_chip *chip)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* without break */
+ default:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ fusb_soft_reset_parameter(chip);
+ set_state(chip, policy_src_send_caps);
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_src_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_src_send_softreset(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_SOFTRESET, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* without break */
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->timer_state = T_SENDER_RESPONSE;
+ chip->sub_state++;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_src_send_hardrst);
+ }
+
+ if (!(evt & FLAG_EVENT))
+ break;
+ default:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_ACCEPT)) {
+ fusb_soft_reset_parameter(chip);
+ set_state(chip, policy_src_send_caps);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ set_state(chip, policy_src_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_startup(struct fusb30x_chip *chip, u32 evt)
+{
+ chip->notify.is_pd_connected = false;
+ fusb_soft_reset_parameter(chip);
+
+ memset(chip->partner_cap, 0, sizeof(chip->partner_cap));
+
+ tcpm_set_msg_header(chip);
+ tcpm_set_polarity(chip, chip->cc_polarity);
+ tcpm_set_rx_enable(chip, 1);
+ set_state(chip, policy_snk_discovery);
+ platform_fusb_notify(chip);
+}
+
+static void fusb_state_snk_discovery(struct fusb30x_chip *chip, u32 evt)
+{
+ set_state(chip, policy_snk_wait_caps);
+ chip->timer_state = T_TYPEC_SINK_WAIT_CAP;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+}
+
+static void fusb_state_snk_wait_caps(struct fusb30x_chip *chip, u32 evt)
+{
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_DATA_MSG(chip->rec_head,
+ DMT_SOURCECAPABILITIES)) {
+ chip->is_pd_support = true;
+ chip->timer_mux = T_DISABLED;
+ set_state(chip, policy_snk_evaluate_caps);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ if (chip->hardrst_count <= N_HARDRESET_COUNT) {
+ if (chip->vbus_begin) {
+ chip->vbus_begin = false;
+ set_state(chip, policy_snk_send_softrst);
+ } else {
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ } else {
+ if (chip->is_pd_support)
+ set_state(chip, error_recovery);
+ else
+ set_state(chip, disabled);
+ }
+ } else if ((evt & EVENT_TIMER_MUX) &&
+ (chip->hardrst_count > N_HARDRESET_COUNT)) {
+ if (chip->is_pd_support)
+ set_state(chip, error_recovery);
+ else
+ set_state(chip, disabled);
+ }
+}
+
+static void fusb_state_snk_evaluate_caps(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ chip->hardrst_count = 0;
+ chip->pos_power = 0;
+
+ for (tmp = 0; tmp < PD_HEADER_CNT(chip->rec_head); tmp++) {
+ switch (CAP_POWER_TYPE(chip->rec_load[tmp])) {
+ case 0:
+ /* Fixed Supply */
+ if (CAP_FPDO_VOLTAGE(chip->rec_load[tmp]) <= 100)
+ chip->pos_power = tmp + 1;
+ break;
+ case 1:
+ /* Battery */
+ if (CAP_VPDO_VOLTAGE(chip->rec_load[tmp]) <= 100)
+ chip->pos_power = tmp + 1;
+ break;
+ default:
+ /* not meet battery caps */
+ break;
+ }
+ }
+ fusb302_set_pos_power_by_charge_ic(chip);
+
+ if ((!chip->pos_power) || (chip->pos_power > 7)) {
+ chip->pos_power = 0;
+ set_state(chip, policy_snk_wait_caps);
+ } else {
+ set_state(chip, policy_snk_select_cap);
+ }
+}
+
+static void fusb_state_snk_select_cap(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, DMT_REQUEST, DATAMESSAGE);
+ chip->sub_state = 1;
+ chip->tx_state = tx_idle;
+ /* without break */
+ case 1:
+ tmp = policy_send_data(chip);
+
+ if (tmp == tx_success) {
+ chip->timer_state = T_SENDER_RESPONSE;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_snk_discovery);
+ break;
+ }
+
+ if (!(evt & FLAG_EVENT))
+ break;
+ default:
+ if (evt & EVENT_RX) {
+ if (!PD_HEADER_CNT(chip->rec_head)) {
+ switch (PD_HEADER_TYPE(chip->rec_head)) {
+ case CMT_ACCEPT:
+ set_state(chip,
+ policy_snk_transition_sink);
+ chip->timer_state = T_PS_TRANSITION;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ break;
+ case CMT_WAIT:
+ case CMT_REJECT:
+ if (chip->notify.is_pd_connected) {
+ dev_info(chip->dev,
+ "PD connected as UFP, fetching 5V\n");
+ set_state(chip,
+ policy_snk_ready);
+ } else {
+ set_state(chip,
+ policy_snk_wait_caps);
+ /*
+ * make sure don't send
+ * hard reset to prevent
+ * infinite loop
+ */
+ chip->hardrst_count =
+ N_HARDRESET_COUNT + 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_transition_sink(struct fusb30x_chip *chip, u32 evt)
+{
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PS_RDY)) {
+ chip->notify.is_pd_connected = true;
+ dev_info(chip->dev,
+ "PD connected as UFP, fetching 5V\n");
+ set_state(chip, policy_snk_ready);
+ } else if (PACKET_IS_DATA_MSG(chip->rec_head,
+ DMT_SOURCECAPABILITIES)) {
+ set_state(chip, policy_snk_evaluate_caps);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ set_state(chip, policy_snk_send_hardrst);
+ }
+}
+
+static void fusb_state_snk_transition_default(struct fusb30x_chip *chip,
+ u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->notify.is_pd_connected = false;
+ chip->timer_mux = T_NO_RESPONSE;
+ fusb_timer_start(&chip->timer_mux_machine,
+ chip->timer_mux);
+ chip->timer_state = T_PS_HARD_RESET_MAX + T_SAFE_0V;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ if (chip->notify.data_role)
+ tcpm_set_msg_header(chip);
+
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ if (!tcpm_check_vbus(chip)) {
+ chip->sub_state++;
+ chip->timer_state = T_SRC_RECOVER_MAX + T_SRC_TURN_ON;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (evt & EVENT_TIMER_STATE) {
+ set_state(chip, policy_snk_startup);
+ }
+ break;
+ default:
+ if (tcpm_check_vbus(chip)) {
+ chip->timer_state = T_DISABLED;
+ set_state(chip, policy_snk_startup);
+ } else if (evt & EVENT_TIMER_STATE) {
+ set_state(chip, policy_snk_startup);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_ready(struct fusb30x_chip *chip, u32 evt)
+{
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_VENDERDEFINED)) {
+ process_vdm_msg(chip);
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ chip->timer_state = T_DISABLED;
+ } else if (!VDM_IS_ACTIVE(chip)) {
+ fusb_state_swap_msg_process(chip, evt);
+ }
+ }
+
+ if (VDM_IS_ACTIVE(chip))
+ auto_vdm_machine(chip, evt);
+
+ fusb_state_swap_msg_process(chip, evt);
+ platform_fusb_notify(chip);
+}
+
+static void fusb_state_snk_send_hardreset(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ default:
+ tmp = policy_send_hardrst(chip, evt);
+ if (tmp == tx_success) {
+ chip->hardrst_count++;
+ set_state(chip, policy_snk_transition_default);
+ } else if (tmp == tx_failed) {
+ set_state(chip, error_recovery);
+ }
+ break;
+ }
+}
+
+static void fusb_state_send_swap(struct fusb30x_chip *chip, u32 evt, int cmd)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, cmd, CONTROLMESSAGE);
+ chip->sub_state = 1;
+ chip->tx_state = tx_idle;
+ /* fallthrough */
+ case 1:
+ tmp = policy_send_data(chip);
+
+ if (tmp == tx_success) {
+ chip->timer_state = T_SENDER_RESPONSE;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ } else if (tmp == tx_failed) {
+ if (cmd == CMT_DR_SWAP) {
+ set_state(chip, error_recovery);
+ return;
+ }
+
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_send_softrst);
+ else
+ set_state(chip, policy_snk_send_softrst);
+ }
+ break;
+ case 2:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_ACCEPT)) {
+ chip->timer_state = T_DISABLED;
+ if (cmd == CMT_VCONN_SWAP) {
+ set_state(chip, chip->vconn_enabled ?
+ policy_vcs_dfp_wait_for_ufp_vconn :
+ policy_vcs_dfp_turn_on_vconn);
+ } else if (cmd == CMT_PR_SWAP) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_prs_transition_to_off);
+ else
+ set_state(chip, policy_snk_prs_transition_to_off);
+ chip->notify.power_role = POWER_ROLE_SOURCE;
+ tcpm_set_msg_header(chip);
+ } else if (cmd == CMT_DR_SWAP) {
+ set_state(chip, chip->notify.data_role ?
+ policy_drs_dfp_change :
+ policy_drs_ufp_change);
+ }
+ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_REJECT) ||
+ PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_WAIT)) {
+ chip->timer_state = T_DISABLED;
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_ready);
+ else
+ set_state(chip, policy_snk_ready);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_ready);
+ else
+ set_state(chip, policy_snk_ready);
+ }
+ }
+}
+
+static void fusb_state_snk_prs_transition_to_off(struct fusb30x_chip *chip,
+ u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->timer_state = T_PD_SOURCE_OFF;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_PS_RDY)) {
+ if (chip->role == ROLE_MODE_DRP)
+ set_state(chip,
+ policy_snk_prs_assert_rp);
+ else
+ set_state(chip,
+ policy_snk_prs_source_on);
+ } else {
+ dev_dbg(chip->dev,
+ "rec careless msg: head %x\n",
+ chip->rec_head);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ chip->notify.power_role = POWER_ROLE_SINK;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_prs_assert_rp(struct fusb30x_chip *chip, u32 evt)
+{
+ tcpm_set_cc_pull_mode(chip, CC_PULL_UP);
+ set_state(chip, policy_snk_prs_source_on);
+}
+
+static void fusb_state_snk_prs_source_on(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ /* supply power in 50ms */
+ platform_set_vbus_lvl_enable(chip, 1, 0);
+ chip->sub_state++;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ break;
+ case 1:
+ set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* fallthrough */
+ case 2:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ /* PD spe 6.5.10.2 */
+ chip->timer_state = T_PD_SWAP_SOURCE_START;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ } else if (tmp == tx_failed) {
+ chip->notify.power_role = POWER_ROLE_SINK;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ case 3:
+ if (evt & EVENT_TIMER_STATE) {
+ chip->cc_state &= ~CC_STATE_TOGSS_IS_UFP;
+ regmap_update_bits(chip->regmap, FUSB_REG_MASK,
+ MASK_M_COMP_CHNG, 0);
+ set_state(chip, policy_src_send_caps);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_softreset(struct fusb30x_chip *chip)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* without break */
+ default:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ fusb_soft_reset_parameter(chip);
+ chip->timer_state = T_TYPEC_SINK_WAIT_CAP;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ set_state(chip, policy_snk_wait_caps);
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_send_softreset(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_SOFTRESET, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->timer_state = T_SENDER_RESPONSE;
+ chip->sub_state++;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ } else if (tmp == tx_failed) {
+ /* can't reach here */
+ set_state(chip, policy_snk_send_hardrst);
+ }
+
+ if (!(evt & FLAG_EVENT))
+ break;
+ default:
+ if (evt & EVENT_RX) {
+ if ((!PD_HEADER_CNT(chip->rec_head)) &&
+ (PD_HEADER_TYPE(chip->rec_head) == CMT_ACCEPT)) {
+ fusb_soft_reset_parameter(chip);
+ chip->timer_state = T_TYPEC_SINK_WAIT_CAP;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ set_state(chip, policy_snk_wait_caps);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_try_detach(struct fusb30x_chip *chip)
+{
+ int cc1, cc2;
+
+ if ((chip->cc_state & CC_STATE_TOGSS_IS_UFP) &&
+ (chip->conn_state !=
+ policy_snk_transition_default) &&
+ (chip->conn_state !=
+ policy_src_prs_source_off) &&
+ (chip->conn_state != policy_snk_prs_send_swap) &&
+ (chip->conn_state != policy_snk_prs_assert_rp) &&
+ (chip->conn_state != policy_snk_prs_source_on) &&
+ (chip->conn_state != policy_snk_prs_transition_to_off)) {
+ if (!tcpm_check_vbus(chip))
+ set_state_unattached(chip);
+ } else if ((chip->conn_state !=
+ policy_src_transition_default) &&
+ (chip->conn_state !=
+ policy_src_prs_source_off) &&
+ (chip->conn_state != policy_snk_prs_source_on)) {
+ tcpm_get_cc(chip, &cc1, &cc2);
+ if (chip->cc_state & CC_STATE_TOGSS_CC2)
+ cc1 = cc2;
+ if (cc1 == TYPEC_CC_VOLT_OPEN)
+ set_state_unattached(chip);
+ } else {
+ /*
+ * Detached may occurred at swap operations. So, DON'T ignore
+ * the EVENT_CC during swapping at all, check the connection
+ * after it.
+ */
+ chip->work_continue |= EVENT_DELAY_CC;
+ }
+}
+
+static void state_machine_typec(struct fusb30x_chip *chip)
+{
+ u32 evt = 0;
+
+ tcpc_alert(chip, &evt);
+ mux_alert(chip, &evt);
+ if (!evt)
+ goto BACK;
+
+ if (chip->notify.is_cc_connected)
+ if (evt & (EVENT_CC | EVENT_DELAY_CC))
+ fusb_try_detach(chip);
+
+ if (evt & EVENT_RX) {
+ tcpm_get_message(chip);
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_SOFTRESET)) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_softrst);
+ else
+ set_state(chip, policy_snk_softrst);
+ }
+ }
+
+ if (evt & EVENT_TX) {
+ if (chip->tx_state == tx_success)
+ chip->msg_id++;
+ }
+ switch (chip->conn_state) {
+ case disabled:
+ fusb_state_disabled(chip, evt);
+ break;
+ case error_recovery:
+ set_state_unattached(chip);
+ break;
+ case unattached:
+ fusb_state_unattached(chip, evt);
+ break;
+ case attach_wait_sink:
+ fusb_state_attach_wait_sink(chip, evt);
+ break;
+ case attach_wait_source:
+ fusb_state_attach_wait_source(chip, evt);
+ break;
+ case attached_source:
+ fusb_state_attached_source(chip, evt);
+ break;
+ case attached_sink:
+ fusb_state_attached_sink(chip, evt);
+ break;
+ case attach_try_src:
+ fusb_state_try_attach(chip, evt, ROLE_MODE_DFP);
+ break;
+ case attach_try_snk:
+ fusb_state_try_attach(chip, evt, ROLE_MODE_UFP);
+ break;
+
+ /* POWER DELIVERY */
+ case policy_src_startup:
+ fusb_state_src_startup(chip, evt);
+ break;
+ case policy_src_discovery:
+ fusb_state_src_discovery(chip, evt);
+ break;
+ case policy_src_send_caps:
+ fusb_state_src_send_caps(chip, evt);
+ if (chip->conn_state != policy_src_negotiate_cap)
+ break;
+ case policy_src_negotiate_cap:
+ fusb_state_src_negotiate_cap(chip, evt);
+
+ case policy_src_transition_supply:
+ fusb_state_src_transition_supply(chip, evt);
+ break;
+ case policy_src_cap_response:
+ fusb_state_src_cap_response(chip, evt);
+ break;
+ case policy_src_transition_default:
+ fusb_state_src_transition_default(chip, evt);
+ break;
+ case policy_src_ready:
+ fusb_state_src_ready(chip, evt);
+ break;
+ case policy_src_get_sink_caps:
+ fusb_state_src_get_sink_cap(chip, evt);
+ break;
+ case policy_src_send_hardrst:
+ fusb_state_src_send_hardreset(chip, evt);
+ break;
+ case policy_src_send_softrst:
+ fusb_state_src_send_softreset(chip, evt);
+ break;
+ case policy_src_softrst:
+ fusb_state_src_softreset(chip);
+ break;
+
+ /* UFP */
+ case policy_snk_startup:
+ fusb_state_snk_startup(chip, evt);
+ break;
+ case policy_snk_discovery:
+ fusb_state_snk_discovery(chip, evt);
+ break;
+ case policy_snk_wait_caps:
+ fusb_state_snk_wait_caps(chip, evt);
+ break;
+ case policy_snk_evaluate_caps:
+ fusb_state_snk_evaluate_caps(chip, evt);
+ /* without break */
+ case policy_snk_select_cap:
+ fusb_state_snk_select_cap(chip, evt);
+ break;
+ case policy_snk_transition_sink:
+ fusb_state_snk_transition_sink(chip, evt);
+ break;
+ case policy_snk_transition_default:
+ fusb_state_snk_transition_default(chip, evt);
+ break;
+ case policy_snk_ready:
+ fusb_state_snk_ready(chip, evt);
+ break;
+ case policy_snk_send_hardrst:
+ fusb_state_snk_send_hardreset(chip, evt);
+ break;
+ case policy_snk_send_softrst:
+ fusb_state_snk_send_softreset(chip, evt);
+ break;
+ case policy_snk_softrst:
+ fusb_state_snk_softreset(chip);
+ break;
+
+ /*
+ * PD Spec 1.0: PR SWAP: chap 8.3.3.6.3.1/2
+ * VC SWAP: chap 8.3.3.7.1/2
+ */
+ case policy_src_prs_evaluate:
+ case policy_snk_prs_evaluate:
+ fusb_state_prs_evaluate(chip, evt);
+ break;
+ case policy_snk_prs_accept:
+ case policy_src_prs_accept:
+ fusb_state_prs_accept(chip, evt);
+ break;
+ case policy_snk_prs_reject:
+ case policy_src_prs_reject:
+ case policy_vcs_ufp_reject:
+ case policy_drs_dfp_reject:
+ case policy_drs_ufp_reject:
+ fusb_state_prs_reject(chip, evt);
+ break;
+ case policy_src_prs_transition_to_off:
+ fusb_state_src_prs_transition_to_off(chip, evt);
+ break;
+ case policy_src_prs_assert_rd:
+ fusb_state_src_prs_assert_rd(chip, evt);
+ break;
+ case policy_src_prs_source_off:
+ fusb_state_src_prs_source_off(chip, evt);
+ break;
+ case policy_snk_prs_send_swap:
+ case policy_src_prs_send_swap:
+ fusb_state_send_swap(chip, evt, CMT_PR_SWAP);
+ break;
+ case policy_snk_prs_transition_to_off:
+ fusb_state_snk_prs_transition_to_off(chip, evt);
+ break;
+ case policy_snk_prs_assert_rp:
+ fusb_state_snk_prs_assert_rp(chip, evt);
+ break;
+ case policy_snk_prs_source_on:
+ fusb_state_snk_prs_source_on(chip, evt);
+ break;
+ case policy_vcs_ufp_evaluate_swap:
+ fusb_state_vcs_ufp_evaluate_swap(chip, evt);
+ break;
+ case policy_vcs_ufp_accept:
+ fusb_state_vcs_ufp_accept(chip, evt);
+ break;
+ case policy_vcs_ufp_wait_for_dfp_vconn:
+ case policy_vcs_dfp_wait_for_ufp_vconn:
+ fusb_state_vcs_wait_for_vconn(chip, evt);
+ break;
+ case policy_vcs_ufp_turn_off_vconn:
+ case policy_vcs_dfp_turn_off_vconn:
+ fusb_state_vcs_set_vconn(chip, evt, false);
+ break;
+ case policy_vcs_ufp_turn_on_vconn:
+ case policy_vcs_dfp_turn_on_vconn:
+ fusb_state_vcs_set_vconn(chip, evt, true);
+ break;
+ case policy_vcs_ufp_send_ps_rdy:
+ case policy_vcs_dfp_send_ps_rdy:
+ fusb_state_vcs_send_ps_rdy(chip, evt);
+ break;
+ case policy_vcs_dfp_send_swap:
+ fusb_state_send_swap(chip, evt, CMT_VCONN_SWAP);
+ break;
+ case policy_drs_ufp_evaluate:
+ case policy_drs_dfp_evaluate:
+ fusb_state_drs_evaluate(chip, evt);
+ break;
+ case policy_drs_dfp_accept:
+ case policy_drs_ufp_accept:
+ fusb_state_drs_send_accept(chip, evt);
+ break;
+ case policy_drs_dfp_change:
+ case policy_drs_ufp_change:
+ fusb_state_drs_role_change(chip, evt);
+ break;
+ case policy_drs_ufp_send_swap:
+ case policy_drs_dfp_send_swap:
+ fusb_state_send_swap(chip, evt, CMT_DR_SWAP);
+ break;
+
+ default:
+ break;
+ }
+
+BACK:
+ if (chip->work_continue) {
+ queue_work(chip->fusb30x_wq, &chip->work);
+ return;
+ }
+
+ if (!platform_get_device_irq_state(chip))
+ fusb_irq_enable(chip);
+ else
+ queue_work(chip->fusb30x_wq, &chip->work);
+}
+
+static irqreturn_t cc_interrupt_handler(int irq, void *dev_id)
+{
+ struct fusb30x_chip *chip = dev_id;
+
+ queue_work(chip->fusb30x_wq, &chip->work);
+ fusb_irq_disable(chip);
+ return IRQ_HANDLED;
+}
+
+static int fusb_initialize_gpio(struct fusb30x_chip *chip)
+{
+ chip->gpio_int = devm_gpiod_get_optional(chip->dev, "int-n", GPIOD_IN);
+ if (IS_ERR(chip->gpio_int))
+ return PTR_ERR(chip->gpio_int);
+
+ /* some board support vbus with other ways */
+ chip->gpio_vbus_5v = devm_gpiod_get_optional(chip->dev, "vbus-5v",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(chip->gpio_vbus_5v))
+ dev_warn(chip->dev,
+ "Could not get named GPIO for VBus5V!\n");
+ else
+ gpiod_set_raw_value(chip->gpio_vbus_5v, 0);
+
+ chip->gpio_vbus_other = devm_gpiod_get_optional(chip->dev,
+ "vbus-other",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(chip->gpio_vbus_other))
+ dev_warn(chip->dev,
+ "Could not get named GPIO for VBusOther!\n");
+ else
+ gpiod_set_raw_value(chip->gpio_vbus_other, 0);
+
+ chip->gpio_discharge = devm_gpiod_get_optional(chip->dev, "discharge",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(chip->gpio_discharge)) {
+ dev_warn(chip->dev,
+ "Could not get named GPIO for discharge!\n");
+ chip->gpio_discharge = NULL;
+ }
+
+ return 0;
+}
+
+static enum hrtimer_restart fusb_timer_handler(struct hrtimer *timer)
+{
+ int i;
+
+ for (i = 0; i < fusb30x_port_used; i++) {
+ if (timer == &fusb30x_port_info[i]->timer_state_machine) {
+ if (fusb30x_port_info[i]->timer_state != T_DISABLED)
+ fusb30x_port_info[i]->timer_state = 0;
+ break;
+ }
+
+ if (timer == &fusb30x_port_info[i]->timer_mux_machine) {
+ if (fusb30x_port_info[i]->timer_mux != T_DISABLED)
+ fusb30x_port_info[i]->timer_mux = 0;
+ break;
+ }
+ }
+
+ if (i != fusb30x_port_used)
+ queue_work(fusb30x_port_info[i]->fusb30x_wq,
+ &fusb30x_port_info[i]->work);
+
+ return HRTIMER_NORESTART;
+}
+
+static void fusb_initialize_timer(struct fusb30x_chip *chip)
+{
+ hrtimer_init(&chip->timer_state_machine, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ chip->timer_state_machine.function = fusb_timer_handler;
+
+ hrtimer_init(&chip->timer_mux_machine, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ chip->timer_mux_machine.function = fusb_timer_handler;
+
+ chip->timer_state = T_DISABLED;
+ chip->timer_mux = T_DISABLED;
+}
+
+static void fusb302_work_func(struct work_struct *work)
+{
+ struct fusb30x_chip *chip;
+
+ chip = container_of(work, struct fusb30x_chip, work);
+ state_machine_typec(chip);
+}
+
+static int fusb30x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct fusb30x_chip *chip;
+ struct PD_CAP_INFO *pd_cap_info;
+ int ret;
+ char *string[2];
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ if (fusb30x_port_used == 0xff)
+ return -1;
+
+ chip->port_num = fusb30x_port_used++;
+ fusb30x_port_info[chip->port_num] = chip;
+
+ chip->dev = &client->dev;
+ chip->regmap = devm_regmap_init_i2c(client, &fusb302_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ dev_err(&client->dev, "Failed to allocate regmap!\n");
+ return PTR_ERR(chip->regmap);
+ }
+
+ ret = fusb_initialize_gpio(chip);
+ if (ret)
+ return ret;
+
+ fusb_initialize_timer(chip);
+
+ chip->fusb30x_wq = create_workqueue("fusb302_wq");
+ INIT_WORK(&chip->work, fusb302_work_func);
+
+ chip->role = ROLE_MODE_NONE;
+ chip->try_role = ROLE_MODE_NONE;
+ if (!of_property_read_string(chip->dev->of_node, "fusb302,role",
+ (const char **)&string[0])) {
+ if (!strcmp(string[0], "ROLE_MODE_DRP"))
+ chip->role = ROLE_MODE_DRP;
+ else if (!strcmp(string[0], "ROLE_MODE_DFP"))
+ chip->role = ROLE_MODE_DFP;
+ else if (!strcmp(string[0], "ROLE_MODE_UFP"))
+ chip->role = ROLE_MODE_UFP;
+ }
+
+ if (chip->role == ROLE_MODE_NONE) {
+ dev_warn(chip->dev,
+ "Can't get property of role, set role to default DRP\n");
+ chip->role = ROLE_MODE_DRP;
+ string[0] = "ROLE_MODE_DRP";
+ }
+
+ if (!of_property_read_string(chip->dev->of_node, "fusb302,try_role",
+ (const char **)&string[1])) {
+ if (!strcmp(string[1], "ROLE_MODE_DFP"))
+ chip->try_role = ROLE_MODE_DFP;
+ else if (!strcmp(string[1], "ROLE_MODE_UFP"))
+ chip->try_role = ROLE_MODE_UFP;
+ }
+
+ if (chip->try_role == ROLE_MODE_NONE)
+ string[1] = "ROLE_MODE_NONE";
+
+ chip->vconn_supported = true;
+ tcpm_init(chip);
+ tcpm_set_rx_enable(chip, 0);
+ chip->conn_state = unattached;
+ tcpm_set_cc(chip, chip->role);
+
+ chip->n_caps_used = 1;
+ chip->source_power_supply[0] = 0x64;
+ chip->source_max_current[0] = 0x96;
+
+ pd_cap_info = &chip->pd_cap_info;
+ pd_cap_info->dual_role_power = 1;
+ pd_cap_info->data_role_swap = 1;
+
+ pd_cap_info->externally_powered = 1;
+ pd_cap_info->usb_suspend_support = 0;
+ pd_cap_info->usb_communications_cap = 0;
+ pd_cap_info->supply_type = 0;
+ pd_cap_info->peak_current = 0;
+
+ chip->extcon = devm_extcon_dev_allocate(&client->dev, fusb302_cable);
+ if (IS_ERR(chip->extcon)) {
+ dev_err(&client->dev, "allocat extcon failed\n");
+ return PTR_ERR(chip->extcon);
+ }
+
+ ret = devm_extcon_dev_register(&client->dev, chip->extcon);
+ if (ret) {
+ dev_err(&client->dev, "failed to register extcon: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set USB property capability: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set USB_HOST property capability: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set DISP_DP property capability: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_SS);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set USB USB_SS property capability: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_SS);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set USB_HOST USB_SS property capability: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_SS);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set DISP_DP USB_SS property capability: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_CHG_USB_FAST,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set USB_PD property capability: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, chip);
+
+ spin_lock_init(&chip->irq_lock);
+ chip->enable_irq = 1;
+
+ chip->gpio_int_irq = gpiod_to_irq(chip->gpio_int);
+ if (chip->gpio_int_irq < 0) {
+ dev_err(&client->dev,
+ "Unable to request IRQ for INT_N GPIO! %d\n",
+ ret);
+ ret = chip->gpio_int_irq;
+ goto IRQ_ERR;
+ }
+
+ ret = devm_request_threaded_irq(&client->dev,
+ chip->gpio_int_irq,
+ NULL,
+ cc_interrupt_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ client->name,
+ chip);
+ if (ret) {
+ dev_err(&client->dev, "irq request failed\n");
+ goto IRQ_ERR;
+ }
+
+ dev_info(chip->dev,
+ "port %d probe success with role %s, try_role %s\n",
+ chip->port_num, string[0], string[1]);
+
+ return 0;
+
+IRQ_ERR:
+ destroy_workqueue(chip->fusb30x_wq);
+ return ret;
+}
+
+static int fusb30x_remove(struct i2c_client *client)
+{
+ struct fusb30x_chip *chip = i2c_get_clientdata(client);
+
+ destroy_workqueue(chip->fusb30x_wq);
+ return 0;
+}
+
+static void fusb30x_shutdown(struct i2c_client *client)
+{
+ struct fusb30x_chip *chip = i2c_get_clientdata(client);
+
+ if (chip->gpio_vbus_5v)
+ gpiod_set_value(chip->gpio_vbus_5v, 0);
+ if (chip->gpio_discharge) {
+ gpiod_set_value(chip->gpio_discharge, 1);
+ msleep(100);
+ gpiod_set_value(chip->gpio_discharge, 0);
+ }
+}
+
+static const struct of_device_id fusb30x_dt_match[] = {
+ { .compatible = FUSB30X_I2C_DEVICETREE_NAME },
+ {},
+};
+MODULE_DEVICE_TABLE(of, fusb30x_dt_match);
+
+static const struct i2c_device_id fusb30x_i2c_device_id[] = {
+ { FUSB30X_I2C_DRIVER_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, fusb30x_i2c_device_id);
+
+static struct i2c_driver fusb30x_driver = {
+ .driver = {
+ .name = FUSB30X_I2C_DRIVER_NAME,
+ .of_match_table = of_match_ptr(fusb30x_dt_match),
+ },
+ .probe = fusb30x_probe,
+ .remove = fusb30x_remove,
+ .shutdown = fusb30x_shutdown,
+ .id_table = fusb30x_i2c_device_id,
+};
+
+module_i2c_driver(fusb30x_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("zain wang <zain.wang@rock-chips.com>");
+MODULE_DESCRIPTION("fusb302 typec pd driver");
diff --git a/drivers/staging/fusb30x/fusb30x.h b/drivers/staging/fusb30x/fusb30x.h
new file mode 100644
index 000000000..4f5ca64f7
--- /dev/null
+++ b/drivers/staging/fusb30x/fusb30x.h
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Zain Wang <zain.wang@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Some ideas are from chrome ec and fairchild GPL fusb302 driver.
+ */
+
+#ifndef FUSB302_H
+#define FUSB302_H
+
+#include <linux/i2c.h>
+#include <linux/hrtimer.h>
+
+const char *FUSB_DT_INTERRUPT_INTN = "fsc_interrupt_int_n";
+#define FUSB_DT_GPIO_INTN "fairchild,int_n"
+#define FUSB_DT_GPIO_VBUS_5V "fairchild,vbus5v"
+#define FUSB_DT_GPIO_VBUS_OTHER "fairchild,vbusOther"
+
+#define FUSB30X_I2C_DRIVER_NAME "fusb302"
+#define FUSB30X_I2C_DEVICETREE_NAME "fairchild,fusb302"
+
+/* FUSB300 Register Addresses */
+#define FUSB_REG_DEVICEID 0x01
+#define FUSB_REG_SWITCHES0 0x02
+#define FUSB_REG_SWITCHES1 0x03
+#define FUSB_REG_MEASURE 0x04
+#define FUSB_REG_SLICE 0x05
+#define FUSB_REG_CONTROL0 0x06
+#define FUSB_REG_CONTROL1 0x07
+#define FUSB_REG_CONTROL2 0x08
+#define FUSB_REG_CONTROL3 0x09
+#define FUSB_REG_MASK 0x0A
+#define FUSB_REG_POWER 0x0B
+#define FUSB_REG_RESET 0x0C
+#define FUSB_REG_OCPREG 0x0D
+#define FUSB_REG_MASKA 0x0E
+#define FUSB_REG_MASKB 0x0F
+#define FUSB_REG_CONTROL4 0x10
+#define FUSB_REG_STATUS0A 0x3C
+#define FUSB_REG_STATUS1A 0x3D
+#define FUSB_REG_INTERRUPTA 0x3E
+#define FUSB_REG_INTERRUPTB 0x3F
+#define FUSB_REG_STATUS0 0x40
+#define FUSB_REG_STATUS1 0x41
+#define FUSB_REG_INTERRUPT 0x42
+#define FUSB_REG_FIFO 0x43
+
+enum connection_state {
+ disabled = 0,
+ error_recovery,
+ unattached,
+ attach_wait_sink,
+ attach_wait_source,
+ attached_source,
+ attached_sink,
+
+ policy_src_startup,
+ policy_src_send_caps,
+ policy_src_discovery,
+ policy_src_negotiate_cap,
+ policy_src_cap_response,
+ policy_src_transition_supply,
+ policy_src_transition_default,
+
+ policy_src_ready,
+ policy_src_get_sink_caps,
+
+ policy_src_send_softrst,
+ policy_src_softrst,
+ policy_src_send_hardrst,
+
+ policy_snk_startup,
+ policy_snk_discovery,
+ policy_snk_wait_caps,
+ policy_snk_evaluate_caps,
+ policy_snk_select_cap,
+ policy_snk_transition_sink,
+ policy_snk_ready,
+
+ policy_snk_send_softrst,
+ policy_snk_softrst,
+ policy_snk_send_hardrst,
+
+ policy_snk_transition_default,
+
+ /* PR SWAP */
+ policy_src_prs_evaluate,
+ policy_src_prs_accept,
+ policy_src_prs_transition_to_off,
+ policy_src_prs_source_off,
+ policy_src_prs_assert_rd,
+ policy_src_prs_reject,
+ policy_src_prs_send_swap,
+
+ policy_snk_prs_evaluate,
+ policy_snk_prs_accept,
+ policy_snk_prs_transition_to_off,
+ policy_snk_prs_source_on,
+ policy_snk_prs_assert_rp,
+ policy_snk_prs_reject,
+ policy_snk_prs_send_swap,
+
+ /* VC SWAP */
+ policy_vcs_dfp_send_swap,
+ policy_vcs_dfp_wait_for_ufp_vconn,
+ policy_vcs_dfp_turn_off_vconn,
+ policy_vcs_dfp_turn_on_vconn,
+ policy_vcs_dfp_send_ps_rdy,
+
+ policy_vcs_ufp_evaluate_swap,
+ policy_vcs_ufp_reject,
+ policy_vcs_ufp_accept,
+ policy_vcs_ufp_wait_for_dfp_vconn,
+ policy_vcs_ufp_turn_off_vconn,
+ policy_vcs_ufp_turn_on_vconn,
+ policy_vcs_ufp_send_ps_rdy,
+
+ policy_drs_ufp_evaluate,
+ policy_drs_ufp_accept,
+ policy_drs_ufp_reject,
+ policy_drs_ufp_change,
+ policy_drs_ufp_send_swap,
+
+ policy_drs_dfp_evaluate,
+ policy_drs_dfp_accept,
+ policy_drs_dfp_reject,
+ policy_drs_dfp_change,
+ policy_drs_dfp_send_swap,
+
+ attach_try_src,
+ attach_try_snk,
+};
+
+enum vdm_state {
+ VDM_STATE_DISCOVERY_ID,
+ VDM_STATE_DISCOVERY_SVID,
+ VDM_STATE_DISCOVERY_MODES,
+ VDM_STATE_ENTER_MODE,
+ VDM_STATE_UPDATE_STATUS,
+ VDM_STATE_DP_CONFIG,
+ VDM_STATE_NOTIFY,
+ VDM_STATE_READY,
+ VDM_STATE_ERR,
+};
+
+enum tcpm_rp_value {
+ TYPEC_RP_USB = 0,
+ TYPEC_RP_1A5 = 1,
+ TYPEC_RP_3A0 = 2,
+ TYPEC_RP_RESERVED = 3,
+};
+
+enum role_mode {
+ ROLE_MODE_NONE,
+ ROLE_MODE_DRP,
+ ROLE_MODE_UFP,
+ ROLE_MODE_DFP,
+ ROLE_MODE_ASS,
+};
+
+#define SBF(s, v) ((s) << (v))
+#define SWITCHES0_PDWN1 SBF(1, 0)
+#define SWITCHES0_PDWN2 SBF(1, 1)
+#define SWITCHES0_MEAS_CC1 SBF(1, 2)
+#define SWITCHES0_MEAS_CC2 SBF(1, 3)
+#define SWITCHES0_VCONN_CC1 SBF(1, 4)
+#define SWITCHES0_VCONN_CC2 SBF(1, 5)
+#define SWITCHES0_PU_EN1 SBF(1, 6)
+#define SWITCHES0_PU_EN2 SBF(1, 7)
+
+#define SWITCHES1_TXCC1 SBF(1, 0)
+#define SWITCHES1_TXCC2 SBF(1, 1)
+#define SWITCHES1_AUTO_CRC SBF(1, 2)
+#define SWITCHES1_DATAROLE SBF(1, 4)
+#define SWITCHES1_SPECREV SBF(3, 5)
+#define SWITCHES1_POWERROLE SBF(1, 7)
+
+#define MEASURE_MDAC SBF(0x3f, 0)
+#define MEASURE_VBUS SBF(1, 6)
+
+#define SLICE_SDAC SBF(0x3f, 0)
+#define SLICE_SDAC_HYS SBF(3, 6)
+
+#define CONTROL0_TX_START SBF(1, 0)
+#define CONTROL0_AUTO_PRE SBF(1, 1)
+#define CONTROL0_HOST_CUR SBF(3, 2)
+#define CONTROL0_HOST_CUR_USB SBF(1, 2)
+#define CONTROL0_HOST_CUR_1A5 SBF(2, 2)
+#define CONTROL0_HOST_CUR_3A0 SBF(3, 2)
+#define CONTROL0_INT_MASK SBF(1, 5)
+#define CONTROL0_TX_FLUSH SBF(1, 6)
+
+#define CONTROL1_ENSOP1 SBF(1, 0)
+#define CONTROL1_ENSOP2 SBF(1, 1)
+#define CONTROL1_RX_FLUSH SBF(1, 2)
+#define CONTROL1_BIST_MODE2 SBF(1, 4)
+#define CONTROL1_ENSOP1DB SBF(1, 5)
+#define CONTROL1_ENSOP2DB SBF(1, 6)
+
+#define CONTROL2_TOGGLE SBF(1, 0)
+#define CONTROL2_MODE SBF(3, 1)
+#define CONTROL2_MODE_NONE 0
+#define CONTROL2_MODE_DFP SBF(3, 1)
+#define CONTROL2_MODE_UFP SBF(2, 1)
+#define CONTROL2_MODE_DRP SBF(1, 1)
+#define CONTROL2_WAKE_EN SBF(1, 3)
+#define CONTROL2_TOG_RD_ONLY SBF(1, 5)
+#define CONTROL2_TOG_SAVE_PWR1 SBF(1, 6)
+#define CONTROL2_TOG_SAVE_PWR2 SBF(1, 7)
+
+#define CONTROL3_AUTO_RETRY SBF(1, 0)
+#define CONTROL3_N_RETRIES SBF(3, 1)
+#define CONTROL3_AUTO_SOFTRESET SBF(1, 3)
+#define CONTROL3_AUTO_HARDRESET SBF(1, 4)
+#define CONTROL3_SEND_HARDRESET SBF(1, 6)
+
+#define MASK_M_BC_LVL SBF(1, 0)
+#define MASK_M_COLLISION SBF(1, 1)
+#define MASK_M_WAKE SBF(1, 2)
+#define MASK_M_ALERT SBF(1, 3)
+#define MASK_M_CRC_CHK SBF(1, 4)
+#define MASK_M_COMP_CHNG SBF(1, 5)
+#define MASK_M_ACTIVITY SBF(1, 6)
+#define MASK_M_VBUSOK SBF(1, 7)
+
+#define POWER_PWR SBF(0xf, 0)
+
+#define RESET_SW_RESET SBF(1, 0)
+#define RESET_PD_RESET SBF(1, 1)
+
+#define MASKA_M_HARDRST SBF(1, 0)
+#define MASKA_M_SOFTRST SBF(1, 1)
+#define MASKA_M_TXSENT SBF(1, 2)
+#define MASKA_M_HARDSENT SBF(1, 3)
+#define MASKA_M_RETRYFAIL SBF(1, 4)
+#define MASKA_M_SOFTFAIL SBF(1, 5)
+#define MASKA_M_TOGDONE SBF(1, 6)
+#define MASKA_M_OCP_TEMP SBF(1, 7)
+
+#define MASKB_M_GCRCSEND SBF(1, 0)
+
+#define CONTROL4_TOG_USRC_EXIT SBF(1, 0)
+
+#define MDAC_1P6V 0x26
+
+#define STATUS0A_HARDRST SBF(1, 0)
+#define STATUS0A_SOFTRST SBF(1, 1)
+#define STATUS0A_POWER23 SBF(3, 2)
+#define STATUS0A_RETRYFAIL SBF(1, 4)
+#define STATUS0A_SOFTFAIL SBF(1, 5)
+#define STATUS0A_TOGDONE SBF(1, 6)
+#define STATUS0A_M_OCP_TEMP SBF(1, 7)
+
+#define STATUS1A_RXSOP SBF(1, 0)
+#define STATUS1A_RXSOP1DB SBF(1, 1)
+#define STATUS1A_RXSOP2DB SBF(1, 2)
+#define STATUS1A_TOGSS SBF(7, 3)
+#define CC_STATE_TOGSS_CC1 SBF(1, 0)
+#define CC_STATE_TOGSS_CC2 SBF(1, 1)
+#define CC_STATE_TOGSS_IS_UFP SBF(1, 2)
+
+#define INTERRUPTA_HARDRST SBF(1, 0)
+#define INTERRUPTA_SOFTRST SBF(1, 1)
+#define INTERRUPTA_TXSENT SBF(1, 2)
+#define INTERRUPTA_HARDSENT SBF(1, 3)
+#define INTERRUPTA_RETRYFAIL SBF(1, 4)
+#define INTERRUPTA_SOFTFAIL SBF(1, 5)
+#define INTERRUPTA_TOGDONE SBF(1, 6)
+#define INTERRUPTA_OCP_TEMP SBF(1, 7)
+
+#define INTERRUPTB_GCRCSENT SBF(1, 0)
+
+#define STATUS0_BC_LVL SBF(3, 0)
+#define STATUS0_WAKE SBF(1, 2)
+#define STATUS0_ALERT SBF(1, 3)
+#define STATUS0_CRC_CHK SBF(1, 4)
+#define STATUS0_COMP SBF(1, 5)
+#define STATUS0_ACTIVITY SBF(1, 6)
+#define STATUS0_VBUSOK SBF(1, 7)
+
+#define STATUS1_OCP SBF(1, 0)
+#define STATUS1_OVRTEMP SBF(1, 1)
+#define STATUS1_TX_FULL SBF(1, 2)
+#define STATUS1_TX_EMPTY SBF(1, 3)
+#define STATUS1_RX_FULL SBF(1, 4)
+#define STATUS1_RX_EMPTY SBF(1, 5)
+#define STATUS1_RXSOP1 SBF(1, 6)
+#define STATUS1_RXSOP2 SBF(1, 7)
+
+#define INTERRUPT_BC_LVL SBF(1, 0)
+#define INTERRUPT_COLLISION SBF(1, 1)
+#define INTERRUPT_WAKE SBF(1, 2)
+#define INTERRUPT_ALERT SBF(1, 3)
+#define INTERRUPT_CRC_CHK SBF(1, 4)
+#define INTERRUPT_COMP_CHNG SBF(1, 5)
+#define INTERRUPT_ACTIVITY SBF(1, 6)
+#define INTERRUPT_VBUSOK SBF(1, 7)
+
+#define FUSB_TKN_TXON 0xa1
+#define FUSB_TKN_SYNC1 0x12
+#define FUSB_TKN_SYNC2 0x13
+#define FUSB_TKN_SYNC3 0x1b
+#define FUSB_TKN_RST1 0x15
+#define FUSB_TKN_RST2 0x16
+#define FUSB_TKN_PACKSYM 0x80
+#define FUSB_TKN_JAMCRC 0xff
+#define FUSB_TKN_EOP 0x14
+#define FUSB_TKN_TXOFF 0xfe
+
+/* USB PD Control Message Types */
+#define CONTROLMESSAGE 0
+#define CMT_GOODCRC 1
+#define CMT_GOTOMIN 2
+#define CMT_ACCEPT 3
+#define CMT_REJECT 4
+#define CMT_PING 5
+#define CMT_PS_RDY 6
+#define CMT_GETSOURCECAP 7
+#define CMT_GETSINKCAP 8
+#define CMT_DR_SWAP 9
+#define CMT_PR_SWAP 10
+#define CMT_VCONN_SWAP 11
+#define CMT_WAIT 12
+#define CMT_SOFTRESET 13
+
+/* USB PD Data Message Types */
+#define DATAMESSAGE 1
+#define DMT_SOURCECAPABILITIES 1
+#define DMT_REQUEST 2
+#define DMT_BIST 3
+#define DMT_SINKCAPABILITIES 4
+#define DMT_VENDERDEFINED 15
+
+/* VDM Command Types */
+#define VDM_DISCOVERY_ID 0X01
+#define VDM_DISCOVERY_SVIDS 0X02
+#define VDM_DISCOVERY_MODES 0X03
+#define VDM_ENTER_MODE 0X04
+#define VDM_EXIT_MODE 0X05
+#define VDM_ATTENTION 0X06
+#define VDM_DP_STATUS_UPDATE 0X10
+#define VDM_DP_CONFIG 0X11
+
+#define VDM_TYPE_INIT 0
+#define VDM_TYPE_ACK 1
+#define VDM_TYPE_NACK 2
+#define VDM_TYPE_BUSY 3
+
+/* 200ms at least, 1 cycle about 6ms */
+#define N_DEBOUNCE_CNT 33
+#define N_CAPS_COUNT 50
+#define N_HARDRESET_COUNT 0
+
+#define T_NO_RESPONSE 5000
+#define T_SRC_RECOVER 830
+#define T_TYPEC_SEND_SOURCECAP 100
+#define T_SENDER_RESPONSE 30
+#define T_SRC_TRANSITION 30
+#define T_TYPEC_SINK_WAIT_CAP 500
+#define T_PS_TRANSITION 500
+#define T_BMC_TIMEOUT 5
+#define T_PS_HARD_RESET_MAX 35
+#define T_SAFE_0V 650
+#define T_SRC_TURN_ON 275
+#define T_SRC_RECOVER_MAX 1000
+#define T_PD_SOURCE_OFF 920
+#define T_PD_SOURCE_ON 480
+#define T_PD_SWAP_SOURCE_START 20
+#define T_PD_VCONN_SRC_ON 100
+#define T_PD_TRY_DRP 75
+
+#define T_NO_TRIGGER 500
+#define T_DISABLED 0xffff
+
+#define PD_HEADER_CNT(header) (((header) >> 12) & 7)
+#define PD_HEADER_TYPE(header) ((header) & 0xF)
+#define PD_HEADER_ID(header) (((header) >> 9) & 7)
+
+#define VDM_HEADER_TYPE(header) (((header) >> 6) & 3)
+#define VDMHEAD_CMD_TYPE_MASK (3 << 6)
+#define VDMHEAD_CMD_MASK (0x1f << 0)
+#define VDMHEAD_STRUCT_TYPE_MASK BIT(15)
+
+#define GET_VDMHEAD_CMD_TYPE(head) ((head & VDMHEAD_CMD_TYPE_MASK) >> 6)
+#define GET_VDMHEAD_CMD(head) (head & VDMHEAD_CMD_MASK)
+#define GET_VDMHEAD_STRUCT_TYPE(head) ((head & VDMHEAD_STRUCT_TYPE_MASK) >> 15)
+
+#define DP_STATUS_MASK 0x000000ff
+#define DP_STATUS_HPD_STATE BIT(7)
+
+#define GET_DP_STATUS(status) (status & DP_STATUS_MASK)
+#define GET_DP_STATUS_HPD(status) ((status & DP_STATUS_HPD_STATE) >> 7)
+
+#define VDM_IDHEAD_USBVID_MASK (0xffff << 0)
+#define VDM_IDHEAD_MODALSUPPORT_MASK BIT(26)
+#define VDM_IDHEAD_PRODUCTTYPE (7 << 27)
+#define VDM_IDHEAD_USBDEVICE BIT(30)
+#define VDM_IDHEAD_USBHOST BIT(30)
+
+#define CAP_POWER_TYPE(PDO) ((PDO >> 30) & 3)
+#define CAP_FPDO_VOLTAGE(PDO) ((PDO >> 10) & 0x3ff)
+#define CAP_VPDO_VOLTAGE(PDO) ((PDO >> 20) & 0x3ff)
+#define CAP_FPDO_CURRENT(PDO) ((PDO >> 0) & 0x3ff)
+#define CAP_VPDO_CURRENT(PDO) ((PDO >> 0) & 0x3ff)
+
+enum CC_ORIENTATION {
+ NONE,
+ CC1,
+ CC2,
+};
+
+enum typec_cc_polarity {
+ TYPEC_POLARITY_CC1,
+ TYPEC_POLARITY_CC2,
+};
+
+enum CC_MODE {
+ CC_PULL_UP,
+ CC_PULL_DOWN,
+ CC_PULL_NONE,
+};
+
+enum typec_power_role {
+ POWER_ROLE_SINK = 0,
+ POWER_ROLE_SOURCE,
+};
+
+enum typec_data_role {
+ DATA_ROLE_UFP = 0,
+ DATA_ROLE_DFP,
+};
+
+struct notify_info {
+ enum CC_ORIENTATION orientation;
+ /* 0 UFP : 1 DFP */
+ enum typec_power_role power_role;
+ enum typec_data_role data_role;
+
+ bool is_cc_connected;
+ bool is_pd_connected;
+
+ bool is_enter_mode;
+ int pin_assignment_support;
+ int pin_assignment_def;
+ bool attention;
+ u32 dp_status;
+ u32 dp_caps;
+};
+
+enum tx_state {
+ tx_idle,
+ tx_busy,
+ tx_failed,
+ tx_success
+};
+
+struct PD_CAP_INFO {
+ u32 peak_current;
+ u32 specification_revision;
+ u32 externally_powered;
+ u32 usb_suspend_support;
+ u32 usb_communications_cap;
+ u32 dual_role_power;
+ u32 data_role_swap;
+ u32 supply_type;
+};
+
+struct fusb30x_chip {
+ struct i2c_client *client;
+ struct device *dev;
+ struct regmap *regmap;
+ struct work_struct work;
+ struct workqueue_struct *fusb30x_wq;
+ struct hrtimer timer_state_machine;
+ struct hrtimer timer_mux_machine;
+ struct PD_CAP_INFO pd_cap_info;
+ struct notify_info notify;
+ struct notify_info notify_cmp;
+ struct extcon_dev *extcon;
+ enum connection_state conn_state;
+ struct gpio_desc *gpio_vbus_5v;
+ struct gpio_desc *gpio_vbus_other;
+ struct gpio_desc *gpio_int;
+ struct gpio_desc *gpio_discharge;
+ int timer_state;
+ int timer_mux;
+ int port_num;
+ u32 work_continue;
+ spinlock_t irq_lock;
+ int gpio_int_irq;
+ int enable_irq;
+
+ /*
+ * ---------------------------------
+ * | role 0x03 << 2, | cc_use 0x03 |
+ * | src 1 << 2, | cc1 1 |
+ * | snk 2 << 2, | cc2 2 |
+ * ---------------------------------
+ */
+ u8 cc_state;
+ int cc1;
+ int cc2;
+ enum typec_cc_polarity cc_polarity;
+ u8 val_tmp;
+ u8 debounce_cnt;
+ int sub_state;
+ int caps_counter;
+ u32 send_load[7];
+ u32 rec_load[7];
+ u16 send_head;
+ u16 rec_head;
+ int msg_id;
+ enum tx_state tx_state;
+ int hardrst_count;
+ u32 source_power_supply[7];
+ /* 50mv unit */
+ u32 source_max_current[7];
+ /* 10ma uint*/
+ int pos_power;
+ /*
+ * if PartnerCap[0] == 0xffffffff
+ * show Partner Device do not support supply
+ */
+ u32 partner_cap[7];
+ int n_caps_used;
+ int vdm_state;
+ int vdm_substate;
+ int vdm_send_state;
+ u16 vdm_svid[12];
+ int vdm_svid_num;
+ u32 vdm_id;
+ u8 chip_id;
+ bool vconn_enabled;
+ bool is_pd_support;
+ int pd_output_vol;
+ int pd_output_cur;
+ int cc_meas_high;
+ int cc_meas_low;
+ bool vbus_begin;
+
+ enum role_mode role;
+ bool vconn_supported;
+ bool try_role_complete;
+ enum role_mode try_role;
+};
+
+#endif /* FUSB302_H */
+

+ 211
- 0
pkgs/os-specific/linux/kernel/nanopi/add-maker-friendlyarm.patch View File

@@ -0,0 +1,211 @@
From 2fc2cbaaaf0dcebdeffa6e87bbc9ad843a5470dd Mon Sep 17 00:00:00 2001
From: hmz007 <hmz007@gmail.com>
Date: Sat, 11 Jan 2020 19:35:03 +0800
Subject: [PATCH] soc: friendlyelec: Add board info driver

Change-Id: I122adb4f99c816b5c177f16392fb2df9c10a47be
Signed-off-by: hmz007 <hmz007@gmail.com>
---
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/friendlyelec/Kconfig | 11 ++
drivers/soc/friendlyelec/Makefile | 1 +
drivers/soc/friendlyelec/board.c | 143 ++++++++++++++++++
8 files changed, 161 insertions(+)
create mode 100644 drivers/soc/friendlyelec/Kconfig
create mode 100644 drivers/soc/friendlyelec/Makefile
create mode 100644 drivers/soc/friendlyelec/board.c

diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 833e04a7835c..9ddbd976395d 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -21,5 +21,6 @@ source "drivers/soc/ux500/Kconfig"
source "drivers/soc/xilinx/Kconfig"
source "drivers/soc/zte/Kconfig"
source "drivers/soc/kendryte/Kconfig"
+source "drivers/soc/friendlyelec/Kconfig"
endmenu
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 2ec355003524..277b5bf269c7 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -27,3 +27,4 @@ obj-$(CONFIG_ARCH_U8500) += ux500/
obj-y += xilinx/
obj-$(CONFIG_ARCH_ZX) += zte/
obj-$(CONFIG_SOC_KENDRYTE) += kendryte/
+obj-$(CONFIG_VENDOR_FRIENDLYELEC) += friendlyelec/
diff --git a/drivers/soc/friendlyelec/Kconfig b/drivers/soc/friendlyelec/Kconfig
new file mode 100644
index 000000000000..9e21c663e6c8
--- /dev/null
+++ b/drivers/soc/friendlyelec/Kconfig
@@ -0,0 +1,11 @@
+#
+# Machine drivers
+#
+
+if ARCH_ROCKCHIP
+
+config VENDOR_FRIENDLYELEC
+ bool "FriendlyElec board based on RK33XX SoCs"
+ default n
+
+endif
diff --git a/drivers/soc/friendlyelec/Makefile b/drivers/soc/friendlyelec/Makefile
new file mode 100644
index 000000000000..870542f05177
--- /dev/null
+++ b/drivers/soc/friendlyelec/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VENDOR_FRIENDLYELEC) += board.o
diff --git a/drivers/soc/friendlyelec/board.c b/drivers/soc/friendlyelec/board.c
new file mode 100644
index 000000000000..886a8e1f7dc0
--- /dev/null
+++ b/drivers/soc/friendlyelec/board.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) Guangzhou FriendlyELEC Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/nvmem-consumer.h>
+
+#define BOARD_MANF "FriendlyELEC Computer Tech. Co., Ltd."
+
+static const char *board_mach;
+static const char *board_name;
+static u32 board_rev;
+static u32 board_serial_high, board_serial_low;
+
+static ssize_t board_sys_info_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ char *s = buf;
+
+ s += sprintf(s, "Hardware\t: %s\n", board_mach);
+ s += sprintf(s, "Revision\t: %04x\n", board_rev);
+ s += sprintf(s, "Serial\t\t: %08x%08x\n",
+ board_serial_high, board_serial_low);
+ s += sprintf(s, "\nModel\t\t: %s\n", board_name);
+ s += sprintf(s, "Manufacturer\t: %s\n", BOARD_MANF);
+
+ return (s - buf);
+}
+
+static struct device_attribute board_attr_info =
+ __ATTR(info, S_IRUGO, board_sys_info_show, NULL);
+
+static int rockchip_cpuinfo_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nvmem_cell *cell;
+ unsigned char *efuse_buf, buf[16];
+ size_t len;
+ int i;
+
+ cell = nvmem_cell_get(dev, "id");
+ if (IS_ERR(cell)) {
+ dev_err(dev, "failed to get id cell: %ld\n", PTR_ERR(cell));
+ return PTR_ERR(cell);
+ }
+
+ efuse_buf = nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+
+ if (len != 16) {
+ kfree(efuse_buf);
+ dev_err(dev, "invalid id len: %zu\n", len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 8; i++) {
+ buf[i] = efuse_buf[1 + (i << 1)];
+ buf[i + 8] = efuse_buf[i << 1];
+ }
+
+ kfree(efuse_buf);
+
+ board_serial_low = crc32(0, buf, 8);
+ board_serial_high = crc32(board_serial_low, buf + 8, 8);
+
+ dev_info(dev, "Serial\t\t: %08x%08x\n",
+ board_serial_high, board_serial_low);
+
+ return 0;
+}
+
+static int board_sys_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *root;
+
+ root = of_find_node_by_path("/");
+
+ of_property_read_u32(np, "hwrev", &board_rev);
+
+ if (of_property_read_string(np, "machine", &board_mach))
+ of_property_read_string(root, "compatible", &board_mach);
+
+ if (of_property_read_string(np, "model", &board_name))
+ of_property_read_string(root, "model", &board_name);
+
+ of_node_put(root);
+
+ rockchip_cpuinfo_probe(pdev);
+
+ device_create_file(&pdev->dev, &board_attr_info);
+
+ return 0;
+}
+
+static const struct of_device_id board_sys_of_match[] = {
+ { .compatible = "friendlyelec,board" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, board_sys_of_match);
+
+static struct platform_driver board_sys_driver = {
+ .probe = board_sys_probe,
+ .driver = {
+ .name = "friendlyelec-board",
+ .of_match_table = board_sys_of_match,
+ },
+};
+
+static int __init board_sys_init(void)
+{
+ return platform_driver_register(&board_sys_driver);
+}
+late_initcall(board_sys_init);
+
+MODULE_AUTHOR("support@friendlyarm.com");
+MODULE_DESCRIPTION("FriendlyElec NanoPi Series Machine Driver");
+MODULE_LICENSE("GPL v2");

+ 106
- 0
pkgs/os-specific/linux/kernel/nanopi/board-nanopi-m4v2-dts-add-sound-card.patch View File

@@ -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

+ 15
- 0
pkgs/os-specific/linux/kernel/nanopi/board-nanopi-m4v2-dts-ethernet-tweak.patch View File

@@ -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>;
};

+ 13
- 0
pkgs/os-specific/linux/kernel/nanopi/fix-spi1-flash-speed.patch View File

@@ -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>;
};
};

+ 82
- 0
pkgs/os-specific/linux/kernel/nanopi/general-add-overlay-compilation-support.patch View File

@@ -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

+ 430
- 0
pkgs/os-specific/linux/kernel/nanopi/general-add-overlay-configfs.patch View File

@@ -0,0 +1,430 @@
Below patch is a squash of following 4 commits borrowed from ayufan's
https://github.com/ayufan-rock64/linux-mainline-kernel repo:

From cc22206776d61948f6984a4f03d8013eb4f92984 Mon Sep 17 00:00:00 2001
From: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
Date: Wed, 3 Dec 2014 13:23:28 +0200
Subject: [PATCH] OF: DT-Overlay configfs interface

This is a port of Pantelis Antoniou's v3 port that makes use of the
new upstreamed configfs support for binary attributes.

Original commit message:

Add a runtime interface to using configfs for generic device tree overlay
usage. With it its possible to use device tree overlays without having
to use a per-platform overlay manager.

Please see Documentation/devicetree/configfs-overlays.txt for more info.

Changes since v2:
- Removed ifdef CONFIG_OF_OVERLAY (since for now it's required)
- Created a documentation entry
- Slight rewording in Kconfig

Changes since v1:
- of_resolve() -> of_resolve_phandles().

Originally-signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
Signed-off-by: Phil Elwell <phil@raspberrypi.org>

DT configfs: Fix build errors on other platforms

Signed-off-by: Phil Elwell <phil@raspberrypi.org>

DT configfs: fix build error

There is an error when compiling rpi-4.6.y branch:
CC drivers/of/configfs.o
drivers/of/configfs.c:291:21: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.default_groups = of_cfs_def_groups,
^
drivers/of/configfs.c:291:21: note: (near initialization for 'of_cfs_subsys.su_group.default_groups.next')

The .default_groups is linked list since commit
1ae1602de028acaa42a0f6ff18d19756f8e825c6.
This commit uses configfs_add_default_group to fix this problem.

Change-Id: Id4903c9725ebc355aadd067bbc32047db622ce61
Signed-off-by: Slawomir Stepien <sst@poczta.fm>

From 7cfb967c52d374ddcb0fc9194f38d1d5f9d8cdd8 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 3 Dec 2017 21:43:03 +0000
Subject: [PATCH] configfs: New of_overlay API

From 8637321fabb045fe8617360ef1b058978b0d8457 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.org>
Date: Mon, 4 Dec 2017 14:07:40 +0000
Subject: [PATCH] SQUASH: config_fs: of_overlay_apply takes a pointer

Signed-off-by: Phil Elwell <phil@raspberrypi.org>

From 274dfabb947ca32116a429c582c74aaee6ff1b5b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= <ayufan@ayufan.eu>
Date: Mon, 14 May 2018 11:28:09 +0200
Subject: [PATCH] ayufan: fix overlayfs to compile on 4.17 kernel

Change-Id: I421887697d2ba6e52aba9100100b7664760e2001

diff --git a/Documentation/devicetree/configfs-overlays.txt b/Documentation/devicetree/configfs-overlays.txt
new file mode 100644
index 0000000000000..5fa43e0643072
--- /dev/null
+++ b/Documentation/devicetree/configfs-overlays.txt
@@ -0,0 +1,31 @@
+Howto use the configfs overlay interface.
+
+A device-tree configfs entry is created in /config/device-tree/overlays
+and and it is manipulated using standard file system I/O.
+Note that this is a debug level interface, for use by developers and
+not necessarily something accessed by normal users due to the
+security implications of having direct access to the kernel's device tree.
+
+* To create an overlay you mkdir the directory:
+
+ # mkdir /config/device-tree/overlays/foo
+
+* Either you echo the overlay firmware file to the path property file.
+
+ # echo foo.dtbo >/config/device-tree/overlays/foo/path
+
+* Or you cat the contents of the overlay to the dtbo file
+
+ # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo
+
+The overlay file will be applied, and devices will be created/destroyed
+as required.
+
+To remove it simply rmdir the directory.
+
+ # rmdir /config/device-tree/overlays/foo
+
+The rationalle of the dual interface (firmware & direct copy) is that each is
+better suited to different use patterns. The firmware interface is what's
+intended to be used by hardware managers in the kernel, while the copy interface
+make sense for developers (since it avoids problems with namespaces).
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 37c2ccbefecdc..d3fc81a40c0e7 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -103,4 +103,11 @@ config OF_OVERLAY
config OF_NUMA
bool
+config OF_CONFIGFS
+ bool "Device Tree Overlay ConfigFS interface"
+ select CONFIGFS_FS
+ select OF_OVERLAY
+ help
+ Enable a simple user-space driven DT overlay interface.
+
endif # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index 663a4af0cccd5..b00a95adf5199 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-y = base.o device.o platform.o property.o
obj-$(CONFIG_OF_KOBJ) += kobj.o
+obj-$(CONFIG_OF_CONFIGFS) += configfs.o
obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
obj-$(CONFIG_OF_FLATTREE) += fdt.o
obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c
new file mode 100644
index 000000000..5dd509e8f
--- /dev/null
+++ b/drivers/of/configfs.c
@@ -0,0 +1,290 @@
+/*
+ * Configfs entries for device-tree
+ *
+ * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/ctype.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/configfs.h>
+#include <linux/types.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/file.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <linux/sizes.h>
+
+#include "of_private.h"
+
+struct cfs_overlay_item {
+ struct config_item item;
+
+ char path[PATH_MAX];
+
+ const struct firmware *fw;
+ struct device_node *overlay;
+ int ov_id;
+
+ void *dtbo;
+ int dtbo_size;
+};
+
+static int create_overlay(struct cfs_overlay_item *overlay, void *blob, u32 blob_size)
+{
+ int err;
+
+ err = of_overlay_fdt_apply(blob, blob_size, &overlay->ov_id);
+ if (err < 0) {
+ pr_err("%s: Failed to create overlay (err=%d)\n",
+ __func__, err);
+ goto out_err;
+ }
+
+out_err:
+ return err;
+}
+
+static inline struct cfs_overlay_item *to_cfs_overlay_item(
+ struct config_item *item)
+{
+ return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
+}
+
+static ssize_t cfs_overlay_item_path_show(struct config_item *item,
+ char *page)
+{
+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+ return sprintf(page, "%s\n", overlay->path);
+}
+
+static ssize_t cfs_overlay_item_path_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+ const char *p = page;
+ char *s;
+ int err;
+
+ /* if it's set do not allow changes */
+ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
+ return -EPERM;
+
+ /* copy to path buffer (and make sure it's always zero terminated */
+ count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
+ overlay->path[sizeof(overlay->path) - 1] = '\0';
+
+ /* strip trailing newlines */
+ s = overlay->path + strlen(overlay->path);
+ while (s > overlay->path && *--s == '\n')
+ *s = '\0';
+
+ pr_debug("%s: path is '%s'\n", __func__, overlay->path);
+
+ err = request_firmware(&overlay->fw, overlay->path, NULL);
+ if (err != 0)
+ goto out_err;
+
+ err = create_overlay(overlay, (void *)overlay->fw->data, overlay->fw->size);
+ if (err != 0)
+ goto out_err;
+
+ return count;
+
+out_err:
+
+ release_firmware(overlay->fw);
+ overlay->fw = NULL;
+
+ overlay->path[0] = '\0';
+ return err;
+}
+
+static ssize_t cfs_overlay_item_status_show(struct config_item *item,
+ char *page)
+{
+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+
+ return sprintf(page, "%s\n",
+ overlay->ov_id >= 0 ? "applied" : "unapplied");
+}
+
+CONFIGFS_ATTR(cfs_overlay_item_, path);
+CONFIGFS_ATTR_RO(cfs_overlay_item_, status);
+
+static struct configfs_attribute *cfs_overlay_attrs[] = {
+ &cfs_overlay_item_attr_path,
+ &cfs_overlay_item_attr_status,
+ NULL,
+};
+
+ssize_t cfs_overlay_item_dtbo_read(struct config_item *item,
+ void *buf, size_t max_count)
+{
+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+
+ pr_debug("%s: buf=%p max_count=%zu\n", __func__,
+ buf, max_count);
+
+ if (overlay->dtbo == NULL)
+ return 0;
+
+ /* copy if buffer provided */
+ if (buf != NULL) {
+ /* the buffer must be large enough */
+ if (overlay->dtbo_size > max_count)
+ return -ENOSPC;
+
+ memcpy(buf, overlay->dtbo, overlay->dtbo_size);
+ }
+
+ return overlay->dtbo_size;
+}
+
+ssize_t cfs_overlay_item_dtbo_write(struct config_item *item,
+ const void *buf, size_t count)
+{
+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+ int err;
+
+ /* if it's set do not allow changes */
+ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
+ return -EPERM;
+
+ /* copy the contents */
+ overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
+ if (overlay->dtbo == NULL)
+ return -ENOMEM;
+
+ overlay->dtbo_size = count;
+
+ err = create_overlay(overlay, overlay->dtbo, overlay->dtbo_size);
+ if (err != 0)
+ goto out_err;
+
+ return count;
+
+out_err:
+ kfree(overlay->dtbo);
+ overlay->dtbo = NULL;
+ overlay->dtbo_size = 0;
+
+ return err;
+}
+
+CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M);
+
+static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = {
+ &cfs_overlay_item_attr_dtbo,
+ NULL,
+};
+
+static void cfs_overlay_release(struct config_item *item)
+{
+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+
+ if (overlay->ov_id >= 0)
+ of_overlay_remove(&overlay->ov_id);
+ if (overlay->fw)
+ release_firmware(overlay->fw);
+ /* kfree with NULL is safe */
+ kfree(overlay->dtbo);
+ kfree(overlay);
+}
+
+static struct configfs_item_operations cfs_overlay_item_ops = {
+ .release = cfs_overlay_release,
+};
+
+static struct config_item_type cfs_overlay_type = {
+ .ct_item_ops = &cfs_overlay_item_ops,
+ .ct_attrs = cfs_overlay_attrs,
+ .ct_bin_attrs = cfs_overlay_bin_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *cfs_overlay_group_make_item(
+ struct config_group *group, const char *name)
+{
+ struct cfs_overlay_item *overlay;
+
+ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
+ if (!overlay)
+ return ERR_PTR(-ENOMEM);
+ overlay->ov_id = -1;
+
+ config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
+ return &overlay->item;
+}
+
+static void cfs_overlay_group_drop_item(struct config_group *group,
+ struct config_item *item)
+{
+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+
+ config_item_put(&overlay->item);
+}
+
+static struct configfs_group_operations overlays_ops = {
+ .make_item = cfs_overlay_group_make_item,
+ .drop_item = cfs_overlay_group_drop_item,
+};
+
+static struct config_item_type overlays_type = {
+ .ct_group_ops = &overlays_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_group_operations of_cfs_ops = {
+ /* empty - we don't allow anything to be created */
+};
+
+static struct config_item_type of_cfs_type = {
+ .ct_group_ops = &of_cfs_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+struct config_group of_cfs_overlay_group;
+
+static struct configfs_subsystem of_cfs_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "device-tree",
+ .ci_type = &of_cfs_type,
+ },
+ },
+ .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
+};
+
+static int __init of_cfs_init(void)
+{
+ int ret;
+
+ pr_info("%s\n", __func__);
+
+ config_group_init(&of_cfs_subsys.su_group);
+ config_group_init_type_name(&of_cfs_overlay_group, "overlays",
+ &overlays_type);
+ configfs_add_default_group(&of_cfs_overlay_group,
+ &of_cfs_subsys.su_group);
+
+ ret = configfs_register_subsystem(&of_cfs_subsys);
+ if (ret != 0) {
+ pr_err("%s: failed to register subsys\n", __func__);
+ goto out;
+ }
+ pr_info("%s: OK\n", __func__);
+out:
+ return ret;
+}
+late_initcall(of_cfs_init);

+ 19
- 0
pkgs/os-specific/linux/kernel/nanopi/general-btsdio-ignore-uart-devs.patch View File

@@ -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;
}

+ 22
- 0
pkgs/os-specific/linux/kernel/nanopi/general-emmc-hs400es-init-tweak.patch View File

@@ -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,

+ 11
- 0
pkgs/os-specific/linux/kernel/nanopi/general-fix-es8316-kernel-panic.patch View File

@@ -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);
}

+ 21
- 0
pkgs/os-specific/linux/kernel/nanopi/general-increasing_DMA_block_memory_allocation_to_2048.patch View File

@@ -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);

+ 490
- 0
pkgs/os-specific/linux/kernel/nanopi/general-rockchip-overlays.patch View File

@@ -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))

+ 60
- 0
pkgs/os-specific/linux/kernel/nanopi/general-rt5651-add-mclk.patch View File

@@ -0,0 +1,60 @@
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index c506c9305..41a08b320 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -24,6 +24,7 @@
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/jack.h>
+#include <linux/clk.h>
#include "rl6231.h"
#include "rt5651.h"
@@ -1511,6 +1512,7 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
static int rt5651_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_PREPARE:
if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) {
@@ -1518,6 +1520,13 @@ static int rt5651_set_bias_level(struct snd_soc_component *component,
snd_soc_component_update_bits(component, RT5651_D_MISC,
0xc00, 0xc00);
}
+ if (!IS_ERR(rt5651->mclk)){
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5651->mclk);
+ } else {
+ clk_prepare_enable(rt5651->mclk);
+ }
+ }
break;
case SND_SOC_BIAS_STANDBY:
if (SND_SOC_BIAS_OFF == snd_soc_component_get_bias_level(component)) {
@@ -2059,6 +2068,13 @@ static int rt5651_probe(struct snd_soc_component *component)
{
struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+ /* Check if MCLK provided */
+ rt5651->mclk = devm_clk_get(component->dev, "mclk");
+ if (PTR_ERR(rt5651->mclk) == -EPROBE_DEFER){
+ dev_err(component->dev, "unable to get mclk\n");
+ return -EPROBE_DEFER;
+ }
+
rt5651->component = component;
snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h
index 20c33a3ec..17524fa9f 100644
--- a/sound/soc/codecs/rt5651.h
+++ b/sound/soc/codecs/rt5651.h
@@ -2097,6 +2097,7 @@ struct rt5651_priv {
int dmic_en;
bool hp_mute;
+ struct clk *mclk;
};
#endif /* __RT5651_H__ */

+ 25
- 0
pkgs/os-specific/linux/kernel/nanopi/rk3399-add-sclk-i2sout-src-clock.patch View File

@@ -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

+ 40
- 0
pkgs/os-specific/linux/kernel/nanopi/rk3399-bump-voltages-for-low-opps.patch View File

@@ -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>;

+ 49
- 0
pkgs/os-specific/linux/kernel/nanopi/rk3399-sd-drive-level-8ma.patch View File

@@ -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>;
};
};

+ 1602
- 0
pkgs/os-specific/linux/kernel/nanopi/rkvdec_nv15.patch View File

@@ -0,0 +1,1602 @@
From bbfdef43b6f4a65b960ebe6e540233a4c26e9374 Mon Sep 17 00:00:00 2001
From: Ezequiel Garcia <ezequiel@collabora.com>
Date: Fri, 26 Jun 2020 14:11:29 -0300
Subject: [PATCH 01/18] rkvdec: h264: Refuse to decode unsupported bitstream

The hardware only supports 4:2:2, 4:2:0 or 4:0:0 (monochrome),
8-bit or 10-bit depth content.

Verify that the PPS refers to a supported bitstream, and refuse
unsupported bitstreams by failing at TRY_EXT_CTRLS time.

The driver is currently broken on 10-bit and 4:2:2
so disallow those as well.

Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com>
---
drivers/staging/media/rkvdec/rkvdec.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c
index 225eeca73356..0f81b47792f6 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/staging/media/rkvdec/rkvdec.c
@@ -27,6 +27,32 @@
#include "rkvdec.h"
#include "rkvdec-regs.h"
+static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) {
+ const struct v4l2_ctrl_h264_sps *sps = ctrl->p_cur.p;
+ /*
+ * TODO: The hardware supports 10-bit and 4:2:2 profiles,
+ * but it's currently broken in the driver.
+ * Reject them for now, until it's fixed.
+ */
+ if (sps->chroma_format_idc > 1)
+ /* Only 4:0:0 and 4:2:0 are supported */
+ return -EINVAL;
+ if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
+ /* Luma and chroma bit depth mismatch */
+ return -EINVAL;
+ if (sps->bit_depth_luma_minus8 != 0)
+ /* Only 8-bit is supported */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = {
+ .try_ctrl = rkvdec_try_ctrl,
+};
+
static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = {
{
.per_request = true,
@@ -42,6 +68,7 @@ static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = {
.per_request = true,
.mandatory = true,
.cfg.id = V4L2_CID_MPEG_VIDEO_H264_SPS,
+ .cfg.ops = &rkvdec_ctrl_ops,
},
{
.per_request = true,
--
2.25.1


From 843e3576055776df5cafb3f8e28ea08f9fa71e9a Mon Sep 17 00:00:00 2001
From: Ezequiel Garcia <ezequiel@collabora.com>
Date: Fri, 26 Jun 2020 14:11:30 -0300
Subject: [PATCH 02/18] hantro: h264: Refuse to decode unsupported bitstream

The hardware only supports 4:2:0 or 4:0:0 (monochrome),
8-bit depth content.

Verify that the PPS refers to a supported bitstream, and refuse
unsupported bitstreams by failing at TRY_EXT_CTRLS time.

Given the JPEG compression level control is the only one
that needs setting, a specific ops is provided.

Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com>
---
drivers/staging/media/hantro/hantro_drv.c | 29 ++++++++++++++++++++---
1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c
index 0db8ad455160..361ffaa821ef 100644
--- a/drivers/staging/media/hantro/hantro_drv.c
+++ b/drivers/staging/media/hantro/hantro_drv.c
@@ -261,7 +261,25 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
return vb2_queue_init(dst_vq);
}
-static int hantro_s_ctrl(struct v4l2_ctrl *ctrl)
+static int hantro_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) {
+ const struct v4l2_ctrl_h264_sps *sps = ctrl->p_cur.p;
+
+ if (sps->chroma_format_idc > 1)
+ /* Only 4:0:0 and 4:2:0 are supported */
+ return -EINVAL;
+ if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
+ /* Luma and chroma bit depth mismatch */
+ return -EINVAL;
+ if (sps->bit_depth_luma_minus8 != 0)
+ /* Only 8-bit is supported */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int hantro_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct hantro_ctx *ctx;
@@ -282,7 +300,11 @@ static int hantro_s_ctrl(struct v4l2_ctrl *ctrl)
}
static const struct v4l2_ctrl_ops hantro_ctrl_ops = {
- .s_ctrl = hantro_s_ctrl,
+ .try_ctrl = hantro_try_ctrl,
+};
+
+static const struct v4l2_ctrl_ops hantro_jpeg_ctrl_ops = {
+ .s_ctrl = hantro_jpeg_s_ctrl,
};
static const struct hantro_ctrl controls[] = {
@@ -294,7 +316,7 @@ static const struct hantro_ctrl controls[] = {
.max = 100,
.step = 1,
.def = 50,
- .ops = &hantro_ctrl_ops,
+ .ops = &hantro_jpeg_ctrl_ops,
},
}, {
.codec = HANTRO_MPEG2_DECODER,
@@ -325,6 +347,7 @@ static const struct hantro_ctrl controls[] = {
.codec = HANTRO_H264_DECODER,
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_H264_SPS,
+ .ops = &hantro_ctrl_ops,
},
}, {
.codec = HANTRO_H264_DECODER,
--
2.25.1


From 22400b16c3d7af8cf3b044277264cc5fc3a07c54 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 5 Jul 2020 21:48:56 +0000
Subject: [PATCH 03/18] fixup! rkvdec: h264: Refuse to decode unsupported
bitstream

---
drivers/staging/media/rkvdec/rkvdec.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c
index 0f81b47792f6..55dc27171ce4 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/staging/media/rkvdec/rkvdec.c
@@ -30,7 +30,7 @@
static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
{
if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) {
- const struct v4l2_ctrl_h264_sps *sps = ctrl->p_cur.p;
+ const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p;
/*
* TODO: The hardware supports 10-bit and 4:2:2 profiles,
* but it's currently broken in the driver.
--
2.25.1


From 974229575250290e49d004933cb0aade87fa4566 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:33 +0000
Subject: [PATCH 04/18] media: rkvdec: h264: Fix reference frame_num wrap for
second field

When decoding the second field in a complementary field pair the second
field is sharing the same frame_num with the first field.

Currently the frame_num for the first field is wrapped when it matches the
field being decoded, this cause issues to decode the second field in a
complementary field pair.

Fix this by using inclusive comparison, less than or equal.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Reviewed-by: Ezequiel Garcia <ezequiel@collabora.com>
---
drivers/staging/media/rkvdec/rkvdec-h264.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c
index 7b66e2743a4f..f0cfed84d60d 100644
--- a/drivers/staging/media/rkvdec/rkvdec-h264.c
+++ b/drivers/staging/media/rkvdec/rkvdec-h264.c
@@ -754,7 +754,7 @@ static void assemble_hw_rps(struct rkvdec_ctx *ctx,
continue;
if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM ||
- dpb[i].frame_num < sl_params->frame_num) {
+ dpb[i].frame_num <= sl_params->frame_num) {
p[i] = dpb[i].frame_num;
continue;
}
--
2.25.1


From 031ff8b97b208e20b49554c657e8ca9c79a079db Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:34 +0000
Subject: [PATCH 05/18] media: rkvdec: Ensure decoded resolution fit coded
resolution

Ensure decoded CAPTURE buffer resolution is larger or equal to the coded
OPTUPT buffer resolution.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/staging/media/rkvdec/rkvdec.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c
index 55dc27171ce4..4ab8f7e0566b 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/staging/media/rkvdec/rkvdec.c
@@ -235,6 +235,8 @@ static int rkvdec_try_capture_fmt(struct file *file, void *priv,
pix_mp->pixelformat = coded_desc->decoded_fmts[0];
/* Always apply the frmsize constraint of the coded end. */
+ pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width);
+ pix_mp->height = max(pix_mp->height, ctx->coded_fmt.fmt.pix_mp.height);
v4l2_apply_frmsize_constraints(&pix_mp->width,
&pix_mp->height,
&coded_desc->frmsize);
--
2.25.1


From 4c7271baa21e2e508593c8e87d77febff68afb6a Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:34 +0000
Subject: [PATCH 06/18] media: rkvdec: h264: Validate and use pic width and
height in mbs

The width and height in mbs is currently configured based on OUTPUT buffer
resolution, this works for frame pictures but can cause issues for field
pictures.

When frame_mbs_only_flag is 0 the height in mbs should be height of
the field instead of height of frame.

Validate pic_width_in_mbs_minus1 and pic_height_in_map_units_minus1
against OUTPUT buffer resolution and use these values to configure HW.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/staging/media/rkvdec/rkvdec-h264.c | 4 ++--
drivers/staging/media/rkvdec/rkvdec.c | 10 ++++++++++
2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c
index f0cfed84d60d..3498e9eec3d8 100644
--- a/drivers/staging/media/rkvdec/rkvdec-h264.c
+++ b/drivers/staging/media/rkvdec/rkvdec-h264.c
@@ -672,8 +672,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4);
WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO),
DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG);
- WRITE_PPS(DIV_ROUND_UP(ctx->coded_fmt.fmt.pix_mp.width, 16), PIC_WIDTH_IN_MBS);
- WRITE_PPS(DIV_ROUND_UP(ctx->coded_fmt.fmt.pix_mp.height, 16), PIC_HEIGHT_IN_MBS);
+ WRITE_PPS(sps->pic_width_in_mbs_minus1 + 1, PIC_WIDTH_IN_MBS);
+ WRITE_PPS(sps->pic_height_in_map_units_minus1 + 1, PIC_HEIGHT_IN_MBS);
WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY),
FRAME_MBS_ONLY_FLAG);
WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD),
diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c
index 4ab8f7e0566b..7a9f78bc0a55 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/staging/media/rkvdec/rkvdec.c
@@ -29,8 +29,11 @@
static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl);
+
if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) {
const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p;
+ unsigned int width, height;
/*
* TODO: The hardware supports 10-bit and 4:2:2 profiles,
* but it's currently broken in the driver.
@@ -45,6 +48,13 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
if (sps->bit_depth_luma_minus8 != 0)
/* Only 8-bit is supported */
return -EINVAL;
+
+ width = (sps->pic_width_in_mbs_minus1 + 1) * 16;
+ height = (sps->pic_height_in_map_units_minus1 + 1) * 16;
+
+ if (width > ctx->coded_fmt.fmt.pix_mp.width ||
+ height > ctx->coded_fmt.fmt.pix_mp.height)
+ return -EINVAL;
}
return 0;
}
--
2.25.1


From d0673c50f58c79198b3c2958beaa8b88e2bdfaef Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:35 +0000
Subject: [PATCH 07/18] media: rkvdec: h264: Fix bit depth wrap in pps packet

The luma and chroma bit depth fields in the pps packet is 3 bits wide.
8 is wrongly added to the bit depth value written to these 3-bit fields.
Because only the 3 LSB is written the hardware is configured correctly.

Correct this by not adding 8 to the luma and chroma bit depth value.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Reviewed-by: Ezequiel Garcia <ezequiel@collabora.com>
---
drivers/staging/media/rkvdec/rkvdec-h264.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c
index 3498e9eec3d8..6576b4a101ae 100644
--- a/drivers/staging/media/rkvdec/rkvdec-h264.c
+++ b/drivers/staging/media/rkvdec/rkvdec-h264.c
@@ -662,8 +662,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
WRITE_PPS(0xff, PROFILE_IDC);
WRITE_PPS(1, CONSTRAINT_SET3_FLAG);
WRITE_PPS(sps->chroma_format_idc, CHROMA_FORMAT_IDC);
- WRITE_PPS(sps->bit_depth_luma_minus8 + 8, BIT_DEPTH_LUMA);
- WRITE_PPS(sps->bit_depth_chroma_minus8 + 8, BIT_DEPTH_CHROMA);
+ WRITE_PPS(sps->bit_depth_luma_minus8, BIT_DEPTH_LUMA);
+ WRITE_PPS(sps->bit_depth_chroma_minus8, BIT_DEPTH_CHROMA);
WRITE_PPS(0, QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG);
WRITE_PPS(sps->log2_max_frame_num_minus4, LOG2_MAX_FRAME_NUM_MINUS4);
WRITE_PPS(sps->max_num_ref_frames, MAX_NUM_REF_FRAMES);
--
2.25.1


From c1cb24d82d654583f9b716f8385254d61223aa03 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:35 +0000
Subject: [PATCH 08/18] media: rkvdec: h264: Do not override output buffer
sizeimage

The output buffer sizeimage is currently forced to 2 bytes per pixel, this
can lead to high memory usage for 4K content when multiple output buffers
is created by userspace.

Do not override output buffer sizeimage and let userspace have control of
output buffer sizeimage by only setting sizeimage if none is provided.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/staging/media/rkvdec/rkvdec-h264.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c
index 6576b4a101ae..3a85545bcb38 100644
--- a/drivers/staging/media/rkvdec/rkvdec-h264.c
+++ b/drivers/staging/media/rkvdec/rkvdec-h264.c
@@ -1012,8 +1012,9 @@ static int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx,
struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
fmt->num_planes = 1;
- fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height *
- RKVDEC_H264_MAX_DEPTH_IN_BYTES;
+ if (!fmt->plane_fmt[0].sizeimage)
+ fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height *
+ RKVDEC_H264_MAX_DEPTH_IN_BYTES;
return 0;
}
--
2.25.1


From 1f381d3ddd88174c12846ba93ddd7deb47089a8e Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:35 +0000
Subject: [PATCH 09/18] media: v4l2-common: Add helpers to calculate
bytesperline and sizeimage

Add helper functions to calculate plane bytesperline and sizeimage, these
new helpers consider block width and height when calculating plane
bytesperline and sizeimage.

This prepare support for new pixel formats added in next patch that make
use of block width and height.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/media/v4l2-core/v4l2-common.c | 77 +++++++++++++--------------
1 file changed, 38 insertions(+), 39 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 3dc17ebe14fa..4102c373b48a 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -333,6 +333,33 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf
return info->block_h[plane];
}
+static inline unsigned int v4l2_format_plane_width(const struct v4l2_format_info *info, int plane,
+ unsigned int width)
+{
+ unsigned int hdiv = plane ? info->hdiv : 1;
+ unsigned int bytes = DIV_ROUND_UP(width * info->bpp[plane],
+ v4l2_format_block_width(info, plane) *
+ v4l2_format_block_height(info, plane));
+
+ return DIV_ROUND_UP(bytes, hdiv);
+}
+
+static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_info *info, int plane,
+ unsigned int height)
+{
+ unsigned int vdiv = plane ? info->vdiv : 1;
+ unsigned int lines = ALIGN(height, v4l2_format_block_height(info, plane));
+
+ return DIV_ROUND_UP(lines, vdiv);
+}
+
+static inline unsigned int v4l2_format_plane_size(const struct v4l2_format_info *info, int plane,
+ unsigned int width, unsigned int height)
+{
+ return v4l2_format_plane_width(info, plane, width) *
+ v4l2_format_plane_height(info, plane, height);
+}
+
void v4l2_apply_frmsize_constraints(u32 *width, u32 *height,
const struct v4l2_frmsize_stepwise *frmsize)
{
@@ -368,37 +395,19 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
if (info->mem_planes == 1) {
plane = &pixfmt->plane_fmt[0];
- plane->bytesperline = ALIGN(width, v4l2_format_block_width(info, 0)) * info->bpp[0];
+ plane->bytesperline = v4l2_format_plane_width(info, 0, width);
plane->sizeimage = 0;
- for (i = 0; i < info->comp_planes; i++) {
- unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
- unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
- unsigned int aligned_width;
- unsigned int aligned_height;
-
- aligned_width = ALIGN(width, v4l2_format_block_width(info, i));
- aligned_height = ALIGN(height, v4l2_format_block_height(info, i));
-
- plane->sizeimage += info->bpp[i] *
- DIV_ROUND_UP(aligned_width, hdiv) *
- DIV_ROUND_UP(aligned_height, vdiv);
- }
+ for (i = 0; i < info->comp_planes; i++)
+ plane->sizeimage +=
+ v4l2_format_plane_size(info, i, width, height);
} else {
for (i = 0; i < info->comp_planes; i++) {
- unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
- unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
- unsigned int aligned_width;
- unsigned int aligned_height;
-
- aligned_width = ALIGN(width, v4l2_format_block_width(info, i));
- aligned_height = ALIGN(height, v4l2_format_block_height(info, i));
-
plane = &pixfmt->plane_fmt[i];
plane->bytesperline =
- info->bpp[i] * DIV_ROUND_UP(aligned_width, hdiv);
- plane->sizeimage =
- plane->bytesperline * DIV_ROUND_UP(aligned_height, vdiv);
+ v4l2_format_plane_width(info, i, width);
+ plane->sizeimage = plane->bytesperline *
+ v4l2_format_plane_height(info, i, height);
}
}
return 0;
@@ -422,22 +431,12 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
pixfmt->width = width;
pixfmt->height = height;
pixfmt->pixelformat = pixelformat;
- pixfmt->bytesperline = ALIGN(width, v4l2_format_block_width(info, 0)) * info->bpp[0];
+ pixfmt->bytesperline = v4l2_format_plane_width(info, 0, width);
pixfmt->sizeimage = 0;
- for (i = 0; i < info->comp_planes; i++) {
- unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
- unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
- unsigned int aligned_width;
- unsigned int aligned_height;
-
- aligned_width = ALIGN(width, v4l2_format_block_width(info, i));
- aligned_height = ALIGN(height, v4l2_format_block_height(info, i));
-
- pixfmt->sizeimage += info->bpp[i] *
- DIV_ROUND_UP(aligned_width, hdiv) *
- DIV_ROUND_UP(aligned_height, vdiv);
- }
+ for (i = 0; i < info->comp_planes; i++)
+ pixfmt->sizeimage +=
+ v4l2_format_plane_size(info, i, width, height);
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt);
--
2.25.1


From 778ba4f6cb10b7e4ae2dd71c35dcdd2363d43b21 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:36 +0000
Subject: [PATCH 10/18] media: v4l2: Add NV15 and NV20 pixel formats

Add NV15 and NV20 pixel formats used by the Rockchip Video Decoder for
10-bit buffers.

NV15 and NV20 is a packed 10-bit 4:2:0/4:2:2 semi-planar Y/CbCr format
similar to P010 and P210 but has no padding between components. Instead,
luminance and chrominance samples are grouped into 4s so that each group is
packed into an integer number of bytes:

YYYY = UVUV = 4 * 10 bits = 40 bits = 5 bytes

The '15' and '20' suffix refers to the optimum effective bits per pixel
which is achieved when the total number of luminance samples is a multiple
of 8 for NV15 and 4 for NV20.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
.../userspace-api/media/v4l/pixfmt-nv15.rst | 101 ++++++++++++++++++
.../userspace-api/media/v4l/pixfmt-nv20.rst | 99 +++++++++++++++++
.../userspace-api/media/v4l/yuv-formats.rst | 2 +
drivers/media/v4l2-core/v4l2-common.c | 3 +
drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
include/uapi/linux/videodev2.h | 3 +
6 files changed, 210 insertions(+)
create mode 100644 Documentation/userspace-api/media/v4l/pixfmt-nv15.rst
create mode 100644 Documentation/userspace-api/media/v4l/pixfmt-nv20.rst

diff --git a/Documentation/userspace-api/media/v4l/pixfmt-nv15.rst b/Documentation/userspace-api/media/v4l/pixfmt-nv15.rst
new file mode 100644
index 000000000000..d059db58c6e0
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/pixfmt-nv15.rst
@@ -0,0 +1,101 @@
+.. Permission is granted to copy, distribute and/or modify this
+.. document under the terms of the GNU Free Documentation License,
+.. Version 1.1 or any later version published by the Free Software
+.. Foundation, with no Invariant Sections, no Front-Cover Texts
+.. and no Back-Cover Texts. A copy of the license is included at
+.. Documentation/userspace-api/media/fdl-appendix.rst.
+..
+.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
+
+.. _V4L2-PIX-FMT-NV15:
+
+**************************
+V4L2_PIX_FMT_NV15 ('NV15')
+**************************
+
+Format with ½ horizontal and vertical chroma resolution, also known as
+YUV 4:2:0. One luminance and one chrominance plane with alternating
+chroma samples similar to ``V4L2_PIX_FMT_NV12`` but with 10-bit samples
+that are grouped into four and packed into five bytes.
+
+The '15' suffix refers to the optimum effective bits per pixel which is
+achieved when the total number of luminance samples is a multiple of 8.
+
+
+Description
+===========
+
+This is a packed 10-bit two-plane version of the YUV 4:2:0 format. The
+three components are separated into two sub-images or planes. The Y plane
+is first. The Y plane has five bytes per each group of four pixels. A
+combined CbCr plane immediately follows the Y plane in memory. The CbCr
+plane is the same width, in bytes, as the Y plane (and of the image), but
+is half as tall in pixels. Each CbCr pair belongs to four pixels. For
+example, Cb\ :sub:`00`/Cr\ :sub:`00` belongs to Y'\ :sub:`00`,
+Y'\ :sub:`01`, Y'\ :sub:`10`, Y'\ :sub:`11`.
+
+If the Y plane has pad bytes after each row, then the CbCr plane has as
+many pad bytes after its rows.
+
+**Byte Order.**
+Little endian. Each cell is one byte. Pixels cross the byte boundary.
+
+
+.. flat-table::
+ :header-rows: 0
+ :stub-columns: 0
+
+ * - start + 0:
+ - Y'\ :sub:`00[7:0]`
+ - Y'\ :sub:`01[5:0]`\ Y'\ :sub:`00[9:8]`
+ - Y'\ :sub:`02[3:0]`\ Y'\ :sub:`01[9:6]`
+ - Y'\ :sub:`03[1:0]`\ Y'\ :sub:`02[9:4]`
+ - Y'\ :sub:`03[9:2]`
+ * - start + 5:
+ - Y'\ :sub:`10[7:0]`
+ - Y'\ :sub:`11[5:0]`\ Y'\ :sub:`10[9:8]`
+ - Y'\ :sub:`12[3:0]`\ Y'\ :sub:`11[9:6]`
+ - Y'\ :sub:`13[1:0]`\ Y'\ :sub:`12[9:4]`
+ - Y'\ :sub:`13[9:2]`
+ * - start + 10:
+ - Cb'\ :sub:`00[7:0]`
+ - Cr'\ :sub:`00[5:0]`\ Cb'\ :sub:`00[9:8]`
+ - Cb'\ :sub:`01[3:0]`\ Cr'\ :sub:`00[9:6]`
+ - Cr'\ :sub:`01[1:0]`\ Cb'\ :sub:`01[9:4]`
+ - Cr'\ :sub:`01[9:2]`
+
+
+**Color Sample Location:**
+
+.. flat-table::
+ :header-rows: 0
+ :stub-columns: 0
+
+ * -
+ - 0
+ -
+ - 1
+ - 2
+ -
+ - 3
+ * - 0
+ - Y
+ -
+ - Y
+ - Y
+ -
+ - Y
+ * -
+ -
+ - C
+ -
+ -
+ - C
+ -
+ * - 1
+ - Y
+ -
+ - Y
+ - Y
+ -
+ - Y
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-nv20.rst b/Documentation/userspace-api/media/v4l/pixfmt-nv20.rst
new file mode 100644
index 000000000000..a8123be0baa3
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/pixfmt-nv20.rst
@@ -0,0 +1,99 @@
+.. Permission is granted to copy, distribute and/or modify this
+.. document under the terms of the GNU Free Documentation License,
+.. Version 1.1 or any later version published by the Free Software
+.. Foundation, with no Invariant Sections, no Front-Cover Texts
+.. and no Back-Cover Texts. A copy of the license is included at
+.. Documentation/userspace-api/media/fdl-appendix.rst.
+..
+.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
+
+.. _V4L2-PIX-FMT-NV20:
+
+**************************
+V4L2_PIX_FMT_NV20 ('NV20')
+**************************
+
+Format with ½ horizontal chroma resolution, also known as YUV 4:2:2.
+One luminance and one chrominance plane with alternating chroma samples
+similar to ``V4L2_PIX_FMT_NV16`` but with 10-bit samples
+that are grouped into four and packed into five bytes.
+
+The '20' suffix refers to the optimum effective bits per pixel which is
+achieved when the total number of luminance samples is a multiple of 4.
+
+
+Description
+===========
+
+This is a packed 10-bit two-plane version of the YUV 4:2:2 format. The
+three components are separated into two sub-images or planes. The Y plane
+is first. The Y plane has five bytes per each group of four pixels. A
+combined CbCr plane immediately follows the Y plane in memory. The CbCr
+plane is the same width and height, in bytes, as the Y plane (and of the
+image). Each CbCr pair belongs to two pixels. For example,
+Cb\ :sub:`00`/Cr\ :sub:`00` belongs to Y'\ :sub:`00`, Y'\ :sub:`01`.
+
+If the Y plane has pad bytes after each row, then the CbCr plane has as
+many pad bytes after its rows.
+
+**Byte Order.**
+Little endian. Each cell is one byte. Pixels cross the byte boundary.
+
+
+.. flat-table::
+ :header-rows: 0
+ :stub-columns: 0
+
+ * - start + 0:
+ - Y'\ :sub:`00[7:0]`
+ - Y'\ :sub:`01[5:0]`\ Y'\ :sub:`00[9:8]`
+ - Y'\ :sub:`02[3:0]`\ Y'\ :sub:`01[9:6]`
+ - Y'\ :sub:`03[1:0]`\ Y'\ :sub:`02[9:4]`
+ - Y'\ :sub:`03[9:2]`
+ * - start + 5:
+ - Y'\ :sub:`10[7:0]`
+ - Y'\ :sub:`11[5:0]`\ Y'\ :sub:`10[9:8]`
+ - Y'\ :sub:`12[3:0]`\ Y'\ :sub:`11[9:6]`
+ - Y'\ :sub:`13[1:0]`\ Y'\ :sub:`12[9:4]`
+ - Y'\ :sub:`13[9:2]`
+ * - start + 10:
+ - Cb'\ :sub:`00[7:0]`
+ - Cr'\ :sub:`00[5:0]`\ Cb'\ :sub:`00[9:8]`
+ - Cb'\ :sub:`01[3:0]`\ Cr'\ :sub:`00[9:6]`
+ - Cr'\ :sub:`01[1:0]`\ Cb'\ :sub:`01[9:4]`
+ - Cr'\ :sub:`01[9:2]`
+ * - start + 15:
+ - Cb'\ :sub:`10[7:0]`
+ - Cr'\ :sub:`10[5:0]`\ Cb'\ :sub:`10[9:8]`
+ - Cb'\ :sub:`11[3:0]`\ Cr'\ :sub:`10[9:6]`
+ - Cr'\ :sub:`11[1:0]`\ Cb'\ :sub:`11[9:4]`
+ - Cr'\ :sub:`11[9:2]`
+
+
+**Color Sample Location:**
+
+.. flat-table::
+ :header-rows: 0
+ :stub-columns: 0
+
+ * -
+ - 0
+ -
+ - 1
+ - 2
+ -
+ - 3
+ * - 0
+ - Y
+ - C
+ - Y
+ - Y
+ - C
+ - Y
+ * - 1
+ - Y
+ - C
+ - Y
+ - Y
+ - C
+ - Y
diff --git a/Documentation/userspace-api/media/v4l/yuv-formats.rst b/Documentation/userspace-api/media/v4l/yuv-formats.rst
index 8ee92d0cd769..7cca883f178a 100644
--- a/Documentation/userspace-api/media/v4l/yuv-formats.rst
+++ b/Documentation/userspace-api/media/v4l/yuv-formats.rst
@@ -61,4 +61,6 @@ to brightness information.
pixfmt-nv16
pixfmt-nv16m
pixfmt-nv24
+ pixfmt-nv15
+ pixfmt-nv20
pixfmt-m420
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 4102c373b48a..0caac755d303 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -267,6 +267,9 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_NV15, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 5, 0, 0 }, .hdiv = 2, .vdiv = 2, .block_w = { 4, 2, 0, 0 }, .block_h = { 1, 1, 0, 0 } },
+ { .format = V4L2_PIX_FMT_NV20, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 5, 0, 0 }, .hdiv = 2, .vdiv = 1, .block_w = { 4, 2, 0, 0 }, .block_h = { 1, 1, 0, 0 } },
+
{ .format = V4L2_PIX_FMT_YUV410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
{ .format = V4L2_PIX_FMT_YVU410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
{ .format = V4L2_PIX_FMT_YUV411P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 1 },
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 02bfef0da76d..4657274bb37a 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1315,6 +1315,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_NV61: descr = "Y/CrCb 4:2:2"; break;
case V4L2_PIX_FMT_NV24: descr = "Y/CbCr 4:4:4"; break;
case V4L2_PIX_FMT_NV42: descr = "Y/CrCb 4:4:4"; break;
+ case V4L2_PIX_FMT_NV15: descr = "10-bit Y/CbCr 4:2:0 (Packed)"; break;
+ case V4L2_PIX_FMT_NV20: descr = "10-bit Y/CbCr 4:2:2 (Packed)"; break;
case V4L2_PIX_FMT_NV12M: descr = "Y/CbCr 4:2:0 (N-C)"; break;
case V4L2_PIX_FMT_NV21M: descr = "Y/CrCb 4:2:0 (N-C)"; break;
case V4L2_PIX_FMT_NV16M: descr = "Y/CbCr 4:2:2 (N-C)"; break;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 303805438814..bd23aeaf0706 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -610,6 +610,9 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_NV24 v4l2_fourcc('N', 'V', '2', '4') /* 24 Y/CbCr 4:4:4 */
#define V4L2_PIX_FMT_NV42 v4l2_fourcc('N', 'V', '4', '2') /* 24 Y/CrCb 4:4:4 */
+#define V4L2_PIX_FMT_NV15 v4l2_fourcc('N', 'V', '1', '5') /* 15 Y/CbCr 4:2:0 10-bit packed */
+#define V4L2_PIX_FMT_NV20 v4l2_fourcc('N', 'V', '2', '0') /* 20 Y/CbCr 4:2:2 10-bit packed */
+
/* two non contiguous planes - one Y, one Cr + Cb interleaved */
#define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */
#define V4L2_PIX_FMT_NV21M v4l2_fourcc('N', 'M', '2', '1') /* 21 Y/CrCb 4:2:0 */
--
2.25.1


From ac18ccac6e1a05ecdffd1eb5e8017d92a5eb6c0f Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:36 +0000
Subject: [PATCH 11/18] media: rkvdec: h264: Use bytesperline and buffer height
to calculate stride

Use bytesperline and buffer height to calculate the strides configured.

This does not really change anything other than ensuring the bytesperline
that is signaled to userspace matches what is configured in HW.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/staging/media/rkvdec/rkvdec-h264.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c
index 3a85545bcb38..10756b9d6118 100644
--- a/drivers/staging/media/rkvdec/rkvdec-h264.c
+++ b/drivers/staging/media/rkvdec/rkvdec-h264.c
@@ -891,9 +891,9 @@ static void config_registers(struct rkvdec_ctx *ctx,
dma_addr_t rlc_addr;
dma_addr_t refer_addr;
u32 rlc_len;
- u32 hor_virstride = 0;
- u32 ver_virstride = 0;
- u32 y_virstride = 0;
+ u32 hor_virstride;
+ u32 ver_virstride;
+ u32 y_virstride;
u32 yuv_virstride = 0;
u32 offset;
dma_addr_t dst_addr;
@@ -904,8 +904,8 @@ static void config_registers(struct rkvdec_ctx *ctx,
f = &ctx->decoded_fmt;
dst_fmt = &f->fmt.pix_mp;
- hor_virstride = (sps->bit_depth_luma_minus8 + 8) * dst_fmt->width / 8;
- ver_virstride = round_up(dst_fmt->height, 16);
+ hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
+ ver_virstride = dst_fmt->height;
y_virstride = hor_virstride * ver_virstride;
if (sps->chroma_format_idc == 0)
--
2.25.1


From 62e313c4f82da894647a166a923c32746c2715ad Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:37 +0000
Subject: [PATCH 12/18] media: rkvdec: Extract rkvdec_fill_decoded_pixfmt
helper method

This extract setting decoded pixfmt into a helper method, current code is
replaced with a call to the new helper method.

The helper method is also called from a new function in next patch.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/staging/media/rkvdec/rkvdec.c | 29 ++++++++++++++-------------
1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c
index 7a9f78bc0a55..911132be6d50 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/staging/media/rkvdec/rkvdec.c
@@ -27,6 +27,17 @@
#include "rkvdec.h"
#include "rkvdec-regs.h"
+static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx,
+ struct v4l2_pix_format_mplane *pix_mp)
+{
+ v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat,
+ pix_mp->width, pix_mp->height);
+ pix_mp->plane_fmt[0].sizeimage += 128 *
+ DIV_ROUND_UP(pix_mp->width, 16) *
+ DIV_ROUND_UP(pix_mp->height, 16);
+ pix_mp->field = V4L2_FIELD_NONE;
+}
+
static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
{
struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl);
@@ -179,13 +190,9 @@ static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx)
rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]);
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- v4l2_fill_pixfmt_mp(&f->fmt.pix_mp,
- ctx->coded_fmt_desc->decoded_fmts[0],
- ctx->coded_fmt.fmt.pix_mp.width,
- ctx->coded_fmt.fmt.pix_mp.height);
- f->fmt.pix_mp.plane_fmt[0].sizeimage += 128 *
- DIV_ROUND_UP(f->fmt.pix_mp.width, 16) *
- DIV_ROUND_UP(f->fmt.pix_mp.height, 16);
+ f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width;
+ f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height;
+ rkvdec_fill_decoded_pixfmt(ctx, &f->fmt.pix_mp);
}
static int rkvdec_enum_framesizes(struct file *file, void *priv,
@@ -251,13 +258,7 @@ static int rkvdec_try_capture_fmt(struct file *file, void *priv,
&pix_mp->height,
&coded_desc->frmsize);
- v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat,
- pix_mp->width, pix_mp->height);
- pix_mp->plane_fmt[0].sizeimage +=
- 128 *
- DIV_ROUND_UP(pix_mp->width, 16) *
- DIV_ROUND_UP(pix_mp->height, 16);
- pix_mp->field = V4L2_FIELD_NONE;
+ rkvdec_fill_decoded_pixfmt(ctx, pix_mp);
return 0;
}
--
2.25.1


From ce2a49e0b4908dbc148df81a053bb3eaf8c28048 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:37 +0000
Subject: [PATCH 13/18] media: rkvdec: Lock capture pixel format in s_ctrl and
s_fmt

Add an optional valid_fmt operation that should return the valid
pixelformat of CAPTURE buffers.

This is used in next patch to ensure correct pixelformat is used for 10-bit
and 4:2:2 content.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/staging/media/rkvdec/rkvdec.c | 59 ++++++++++++++++++++++++---
drivers/staging/media/rkvdec/rkvdec.h | 2 +
2 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c
index 911132be6d50..11a88cb6407d 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/staging/media/rkvdec/rkvdec.c
@@ -38,6 +38,16 @@ static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx,
pix_mp->field = V4L2_FIELD_NONE;
}
+static u32 rkvdec_valid_fmt(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ const struct rkvdec_coded_fmt_desc *coded_desc = ctx->coded_fmt_desc;
+
+ if (coded_desc->ops->valid_fmt)
+ return coded_desc->ops->valid_fmt(ctx, ctrl);
+
+ return ctx->valid_fmt;
+}
+
static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
{
struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl);
@@ -60,6 +70,10 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
/* Only 8-bit is supported */
return -EINVAL;
+ if (ctx->valid_fmt && ctx->valid_fmt != rkvdec_valid_fmt(ctx, ctrl))
+ /* Only current valid format */
+ return -EINVAL;
+
width = (sps->pic_width_in_mbs_minus1 + 1) * 16;
height = (sps->pic_height_in_map_units_minus1 + 1) * 16;
@@ -70,8 +84,27 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
return 0;
}
+static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl);
+
+ if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS && !ctx->valid_fmt) {
+ ctx->valid_fmt = rkvdec_valid_fmt(ctx, ctrl);
+ if (ctx->valid_fmt) {
+ struct v4l2_pix_format_mplane *pix_mp;
+
+ pix_mp = &ctx->decoded_fmt.fmt.pix_mp;
+ pix_mp->pixelformat = ctx->valid_fmt;
+ rkvdec_fill_decoded_pixfmt(ctx, pix_mp);
+ }
+ }
+
+ return 0;
+}
+
static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = {
.try_ctrl = rkvdec_try_ctrl,
+ .s_ctrl = rkvdec_s_ctrl,
};
static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = {
@@ -188,6 +221,7 @@ static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx)
{
struct v4l2_format *f = &ctx->decoded_fmt;
+ ctx->valid_fmt = 0;
rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]);
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width;
@@ -243,13 +277,17 @@ static int rkvdec_try_capture_fmt(struct file *file, void *priv,
if (WARN_ON(!coded_desc))
return -EINVAL;
- for (i = 0; i < coded_desc->num_decoded_fmts; i++) {
- if (coded_desc->decoded_fmts[i] == pix_mp->pixelformat)
- break;
- }
+ if (ctx->valid_fmt) {
+ pix_mp->pixelformat = ctx->valid_fmt;
+ } else {
+ for (i = 0; i < coded_desc->num_decoded_fmts; i++) {
+ if (coded_desc->decoded_fmts[i] == pix_mp->pixelformat)
+ break;
+ }
- if (i == coded_desc->num_decoded_fmts)
- pix_mp->pixelformat = coded_desc->decoded_fmts[0];
+ if (i == coded_desc->num_decoded_fmts)
+ pix_mp->pixelformat = coded_desc->decoded_fmts[0];
+ }
/* Always apply the frmsize constraint of the coded end. */
pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width);
@@ -324,6 +362,7 @@ static int rkvdec_s_capture_fmt(struct file *file, void *priv,
return ret;
ctx->decoded_fmt = *f;
+ ctx->valid_fmt = f->fmt.pix_mp.pixelformat;
return 0;
}
@@ -413,6 +452,14 @@ static int rkvdec_enum_capture_fmt(struct file *file, void *priv,
if (WARN_ON(!ctx->coded_fmt_desc))
return -EINVAL;
+ if (ctx->valid_fmt) {
+ if (f->index)
+ return -EINVAL;
+
+ f->pixelformat = ctx->valid_fmt;
+ return 0;
+ }
+
if (f->index >= ctx->coded_fmt_desc->num_decoded_fmts)
return -EINVAL;
diff --git a/drivers/staging/media/rkvdec/rkvdec.h b/drivers/staging/media/rkvdec/rkvdec.h
index 2fc9f46b6910..50e67401fdbe 100644
--- a/drivers/staging/media/rkvdec/rkvdec.h
+++ b/drivers/staging/media/rkvdec/rkvdec.h
@@ -64,6 +64,7 @@ vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf)
struct rkvdec_coded_fmt_ops {
int (*adjust_fmt)(struct rkvdec_ctx *ctx,
struct v4l2_format *f);
+ u32 (*valid_fmt)(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl);
int (*start)(struct rkvdec_ctx *ctx);
void (*stop)(struct rkvdec_ctx *ctx);
int (*run)(struct rkvdec_ctx *ctx);
@@ -97,6 +98,7 @@ struct rkvdec_ctx {
struct v4l2_fh fh;
struct v4l2_format coded_fmt;
struct v4l2_format decoded_fmt;
+ u32 valid_fmt;
const struct rkvdec_coded_fmt_desc *coded_fmt_desc;
struct v4l2_ctrl_handler ctrl_hdl;
struct rkvdec_dev *dev;
--
2.25.1


From 4a5ebc230d6f6ad93699631f2f55abb6d7bff43f Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:37 +0000
Subject: [PATCH 14/18] media: rkvdec: h264: Support High 10 and 4:2:2 profiles

Add support and enable decoding of H264 High 10 and 4:2:2 profiles.

Decoded CAPTURE buffer width is aligned to 64 pixels to accommodate HW
requirement on 10-bit format buffers.

The new valid_fmt operation is implemented and return a valid pixelformat
for the provided SPS control.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/staging/media/rkvdec/rkvdec-h264.c | 20 ++++++++++++++++++++
drivers/staging/media/rkvdec/rkvdec.c | 19 +++++++++----------
2 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c
index 10756b9d6118..0757fc97d1ff 100644
--- a/drivers/staging/media/rkvdec/rkvdec-h264.c
+++ b/drivers/staging/media/rkvdec/rkvdec-h264.c
@@ -1018,6 +1018,25 @@ static int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx,
return 0;
}
+static u32 rkvdec_h264_valid_fmt(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p;
+
+ if (sps->bit_depth_luma_minus8 == 0) {
+ if (sps->chroma_format_idc == 2)
+ return V4L2_PIX_FMT_NV16;
+ else
+ return V4L2_PIX_FMT_NV12;
+ } else if (sps->bit_depth_luma_minus8 == 2) {
+ if (sps->chroma_format_idc == 2)
+ return V4L2_PIX_FMT_NV20;
+ else
+ return V4L2_PIX_FMT_NV15;
+ }
+
+ return 0;
+}
+
static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
@@ -1125,6 +1144,7 @@ static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops = {
.adjust_fmt = rkvdec_h264_adjust_fmt,
+ .valid_fmt = rkvdec_h264_valid_fmt,
.start = rkvdec_h264_start,
.stop = rkvdec_h264_stop,
.run = rkvdec_h264_run,
diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c
index 11a88cb6407d..4faee9262392 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/staging/media/rkvdec/rkvdec.c
@@ -31,7 +31,7 @@ static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx,
struct v4l2_pix_format_mplane *pix_mp)
{
v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat,
- pix_mp->width, pix_mp->height);
+ ALIGN(pix_mp->width, 64), pix_mp->height);
pix_mp->plane_fmt[0].sizeimage += 128 *
DIV_ROUND_UP(pix_mp->width, 16) *
DIV_ROUND_UP(pix_mp->height, 16);
@@ -55,19 +55,15 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) {
const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p;
unsigned int width, height;
- /*
- * TODO: The hardware supports 10-bit and 4:2:2 profiles,
- * but it's currently broken in the driver.
- * Reject them for now, until it's fixed.
- */
- if (sps->chroma_format_idc > 1)
- /* Only 4:0:0 and 4:2:0 are supported */
+
+ if (sps->chroma_format_idc > 2)
+ /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
/* Luma and chroma bit depth mismatch */
return -EINVAL;
- if (sps->bit_depth_luma_minus8 != 0)
- /* Only 8-bit is supported */
+ if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
+ /* Only 8-bit and 10-bit is supported */
return -EINVAL;
if (ctx->valid_fmt && ctx->valid_fmt != rkvdec_valid_fmt(ctx, ctrl))
@@ -157,6 +153,9 @@ static const struct rkvdec_ctrls rkvdec_h264_ctrls = {
static const u32 rkvdec_h264_decoded_fmts[] = {
V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_NV15,
+ V4L2_PIX_FMT_NV16,
+ V4L2_PIX_FMT_NV20,
};
static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = {
--
2.25.1


From 4dc94c5df4663d2120faaddfe1c581b4c3f347b9 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 21:54:38 +0000
Subject: [PATCH 15/18] media: rkvdec: h264: Support profile and level controls

The Rockchip Video Decoder used in RK3399 supports H.264 profiles from
Baseline to High 4:2:2 up to Level 5.1, except for the Extended profile.

Expose the V4L2_CID_MPEG_VIDEO_H264_PROFILE and the
V4L2_CID_MPEG_VIDEO_H264_LEVEL control, so that userspace can query the
driver for the list of supported profiles and level.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Reviewed-by: Ezequiel Garcia <ezequiel@collabora.com>
---
drivers/staging/media/rkvdec/rkvdec.c | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c
index 4faee9262392..b21031535330 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/staging/media/rkvdec/rkvdec.c
@@ -144,6 +144,19 @@ static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = {
.cfg.def = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B,
.cfg.max = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B,
},
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422,
+ .cfg.menu_skip_mask =
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED),
+ .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ .cfg.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .cfg.max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+ },
};
static const struct rkvdec_ctrls rkvdec_h264_ctrls = {
--
2.25.1


From 4bf8f995e8b6f06db483cee2952de5662bb5cf37 Mon Sep 17 00:00:00 2001
From: Ben Davis <ben.davis@arm.com>
Date: Mon, 1 Jun 2020 17:28:17 +0100
Subject: [PATCH 16/18] drm: drm_fourcc: add NV15, Q410, Q401 YUV formats

DRM_FORMAT_NV15 is a 2 plane format suitable for linear and 16x16
block-linear memory layouts (DRM_FORMAT_MOD_SAMSUNG_16_16_TILE). The
format is similar to P010 with 4:2:0 sub-sampling but has no padding
between components. Instead, luminance and chrominance samples are
grouped into 4s so that each group is packed into an integer number
of bytes:

YYYY = UVUV = 4 * 10 bits = 40 bits = 5 bytes

The '15' suffix refers to the optimum effective bits per pixel which is
achieved when the total number of luminance samples is a multiple of 8.

Q410 and Q401 are both 3 plane non-subsampled formats with 16 bits per
component, but only 10 bits are used and 6 are padded. 'Q' is chosen
as the first letter to denote 3 plane YUV444, (and is the next letter
along from P which is usually 2 plane).

V2: Updated block_w of NV15 to {4, 2, 0}
V3: Updated commit message to include specific modifier name

NV15:
Tested-by: Jonas Karlman <jonas@kwiboo.se>

Reviewed-by: Brian Starkey <brian.starkey@arm.com>
Signed-off-by: Ben Davis <ben.davis@arm.com>
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200601162817.18230-1-ben.davis@arm.com
---
drivers/gpu/drm/drm_fourcc.c | 12 ++++++++++++
include/uapi/drm/drm_fourcc.h | 22 ++++++++++++++++++++++
2 files changed, 34 insertions(+)

diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c
index b234bfaeda06..722c7ebe4e88 100644
--- a/drivers/gpu/drm/drm_fourcc.c
+++ b/drivers/gpu/drm/drm_fourcc.c
@@ -274,6 +274,18 @@ const struct drm_format_info *__drm_format_info(u32 format)
{ .format = DRM_FORMAT_YUV420_10BIT, .depth = 0,
.num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 2, .vsub = 2,
.is_yuv = true },
+ { .format = DRM_FORMAT_NV15, .depth = 0,
+ .num_planes = 2, .char_per_block = { 5, 5, 0 },
+ .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2,
+ .vsub = 2, .is_yuv = true },
+ { .format = DRM_FORMAT_Q410, .depth = 0,
+ .num_planes = 3, .char_per_block = { 2, 2, 2 },
+ .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 0,
+ .vsub = 0, .is_yuv = true },
+ { .format = DRM_FORMAT_Q401, .depth = 0,
+ .num_planes = 3, .char_per_block = { 2, 2, 2 },
+ .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 0,
+ .vsub = 0, .is_yuv = true },
};
unsigned int i;
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index 490143500a50..8ba2d9153a94 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -236,6 +236,12 @@ extern "C" {
#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */
#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */
#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */
+/*
+ * 2 plane YCbCr
+ * index 0 = Y plane, [39:0] Y3:Y2:Y1:Y0 little endian
+ * index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian
+ */
+#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */
/*
* 2 plane YCbCr MSB aligned
@@ -265,6 +271,22 @@ extern "C" {
*/
#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane 16 bits per channel */
+/* 3 plane non-subsampled (444) YCbCr
+ * 16 bits per component, but only 10 bits are used and 6 bits are padded
+ * index 0: Y plane, [15:0] Y:x [10:6] little endian
+ * index 1: Cb plane, [15:0] Cb:x [10:6] little endian
+ * index 2: Cr plane, [15:0] Cr:x [10:6] little endian
+ */
+#define DRM_FORMAT_Q410 fourcc_code('Q', '4', '1', '0')
+
+/* 3 plane non-subsampled (444) YCrCb
+ * 16 bits per component, but only 10 bits are used and 6 bits are padded
+ * index 0: Y plane, [15:0] Y:x [10:6] little endian
+ * index 1: Cr plane, [15:0] Cr:x [10:6] little endian
+ * index 2: Cb plane, [15:0] Cb:x [10:6] little endian
+ */
+#define DRM_FORMAT_Q401 fourcc_code('Q', '4', '0', '1')
+
/*
* 3 plane YCbCr
* index 0: Y plane, [7:0] Y
--
2.25.1


From 588b8545dda3cd0ef8f066dcafff3fa7a93e4056 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 22:30:13 +0000
Subject: [PATCH 17/18] drm: drm_fourcc: add NV20 and NV30 YUV formats

DRM_FORMAT_NV20 and DRM_FORMAT_NV30 formats is the 2x1 and non-subsampled
variant of NV15, a 10-bit 2-plane YUV format that has no padding between
components. Instead, luminance and chrominance samples are grouped into 4s
so that each group is packed into an integer number of bytes:

YYYY = UVUV = 4 * 10 bits = 40 bits = 5 bytes

The '20' and '30' suffix refers to the optimum effective bits per pixel
which is achieved when the total number of luminance samples is a multiple
of 4.

V2: Added NV30 format

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Reviewed-by: Sandy Huang <hjc@rock-chips.com>
---
drivers/gpu/drm/drm_fourcc.c | 8 ++++++++
include/uapi/drm/drm_fourcc.h | 2 ++
2 files changed, 10 insertions(+)

diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c
index 722c7ebe4e88..2daf8a304b53 100644
--- a/drivers/gpu/drm/drm_fourcc.c
+++ b/drivers/gpu/drm/drm_fourcc.c
@@ -278,6 +278,14 @@ const struct drm_format_info *__drm_format_info(u32 format)
.num_planes = 2, .char_per_block = { 5, 5, 0 },
.block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2,
.vsub = 2, .is_yuv = true },
+ { .format = DRM_FORMAT_NV20, .depth = 0,
+ .num_planes = 2, .char_per_block = { 5, 5, 0 },
+ .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2,
+ .vsub = 1, .is_yuv = true },
+ { .format = DRM_FORMAT_NV30, .depth = 0,
+ .num_planes = 2, .char_per_block = { 5, 5, 0 },
+ .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 1,
+ .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_Q410, .depth = 0,
.num_planes = 3, .char_per_block = { 2, 2, 2 },
.block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 0,
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index 8ba2d9153a94..c3024c82a3e3 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -242,6 +242,8 @@ extern "C" {
* index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian
*/
#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */
/*
* 2 plane YCbCr MSB aligned
--
2.25.1


From 5e09593745d332f82471e1a763e9b9b2632543eb Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 6 Jul 2020 22:30:13 +0000
Subject: [PATCH 18/18] drm: rockchip: add NV15, NV20 and NV30 support

Add support for displaying 10-bit 4:2:0 and 4:2:2 formats produced by the
Rockchip Video Decoder on RK322X, RK3288, RK3328, RK3368 and RK3399.
Also add support for 10-bit 4:4:4 format while at it.

V2: Added NV30 support

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Reviewed-by: Sandy Huang <hjc@rock-chips.com>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 29 +++++++++++++++++--
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 1 +
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 32 +++++++++++++++++----
3 files changed, 54 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 33463b79a37b..dc2667cfb575 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -261,6 +261,18 @@ static bool has_rb_swapped(uint32_t format)
}
}
+static bool is_fmt_10(uint32_t format)
+{
+ switch (format) {
+ case DRM_FORMAT_NV15:
+ case DRM_FORMAT_NV20:
+ case DRM_FORMAT_NV30:
+ return true;
+ default:
+ return false;
+ }
+}
+
static enum vop_data_format vop_convert_format(uint32_t format)
{
switch (format) {
@@ -276,10 +288,13 @@ static enum vop_data_format vop_convert_format(uint32_t format)
case DRM_FORMAT_BGR565:
return VOP_FMT_RGB565;
case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV15:
return VOP_FMT_YUV420SP;
case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV20:
return VOP_FMT_YUV422SP;
case DRM_FORMAT_NV24:
+ case DRM_FORMAT_NV30:
return VOP_FMT_YUV444SP;
default:
DRM_ERROR("unsupported format[%08x]\n", format);
@@ -922,7 +937,12 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start;
dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
- offset = (src->x1 >> 16) * fb->format->cpp[0];
+ if (fb->format->block_w[0])
+ offset = (src->x1 >> 16) * fb->format->char_per_block[0] /
+ fb->format->block_w[0];
+ else
+ offset = (src->x1 >> 16) * fb->format->cpp[0];
+
offset += (src->y1 >> 16) * fb->pitches[0];
dma_addr = rk_obj->dma_addr + offset + fb->offsets[0];
@@ -948,6 +968,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
}
VOP_WIN_SET(vop, win, format, format);
+ VOP_WIN_SET(vop, win, fmt_10, is_fmt_10(fb->format->format));
VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv);
@@ -964,7 +985,11 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
uv_obj = fb->obj[1];
rk_uv_obj = to_rockchip_obj(uv_obj);
- offset = (src->x1 >> 16) * bpp / hsub;
+ if (fb->format->block_w[1])
+ offset = (src->x1 >> 16) * bpp /
+ fb->format->block_w[1] / hsub;
+ else
+ offset = (src->x1 >> 16) * bpp / hsub;
offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index d03bdb531ef2..db1138da2bd4 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -154,6 +154,7 @@ struct vop_win_phy {
struct vop_reg enable;
struct vop_reg gate;
struct vop_reg format;
+ struct vop_reg fmt_10;
struct vop_reg rb_swap;
struct vop_reg act_info;
struct vop_reg dsp_info;
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 2413deded22c..475c435d659b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -50,6 +50,23 @@ static const uint32_t formats_win_full[] = {
DRM_FORMAT_NV24,
};
+static const uint32_t formats_win_full_10[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_NV12,
+ DRM_FORMAT_NV16,
+ DRM_FORMAT_NV24,
+ DRM_FORMAT_NV15,
+ DRM_FORMAT_NV20,
+ DRM_FORMAT_NV30,
+};
+
static const uint64_t format_modifiers_win_full[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID,
@@ -570,11 +587,12 @@ static const struct vop_scl_regs rk3288_win_full_scl = {
static const struct vop_win_phy rk3288_win01_data = {
.scl = &rk3288_win_full_scl,
- .data_formats = formats_win_full,
- .nformats = ARRAY_SIZE(formats_win_full),
+ .data_formats = formats_win_full_10,
+ .nformats = ARRAY_SIZE(formats_win_full_10),
.format_modifiers = format_modifiers_win_full,
.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
+ .fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 4),
.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
.dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0),
@@ -704,11 +722,12 @@ static const struct vop_intr rk3368_vop_intr = {
static const struct vop_win_phy rk3368_win01_data = {
.scl = &rk3288_win_full_scl,
- .data_formats = formats_win_full,
- .nformats = ARRAY_SIZE(formats_win_full),
+ .data_formats = formats_win_full_10,
+ .nformats = ARRAY_SIZE(formats_win_full_10),
.format_modifiers = format_modifiers_win_full,
.enable = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 0),
.format = VOP_REG(RK3368_WIN0_CTRL0, 0x7, 1),
+ .fmt_10 = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 4),
.rb_swap = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 12),
.x_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 21),
.y_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 22),
@@ -853,11 +872,12 @@ static const struct vop_win_yuv2yuv_data rk3399_vop_big_win_yuv2yuv_data[] = {
static const struct vop_win_phy rk3399_win01_data = {
.scl = &rk3288_win_full_scl,
- .data_formats = formats_win_full,
- .nformats = ARRAY_SIZE(formats_win_full),
+ .data_formats = formats_win_full_10,
+ .nformats = ARRAY_SIZE(formats_win_full_10),
.format_modifiers = format_modifiers_win_full_afbc,
.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
+ .fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 4),
.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
.y_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 22),
.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
--
2.25.1


+ 25
- 0
pkgs/os-specific/linux/kernel/nanopi/unlock-temperature.patch View File

@@ -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";
};

+ 35
- 0
pkgs/os-specific/linux/kernel/nanopi/zzz-0004-Add-DT-link-for-backward-compatibility.patch View File

@@ -0,0 +1,35 @@
From e3ffe88f90644eddd0fa6ee8c9f324d54c3b7c39 Mon Sep 17 00:00:00 2001
From: Igor Pecovnik <igor.pecovnik@gmail.com>
Date: Tue, 4 Aug 2020 19:35:47 +0200
Subject: [PATCH] Add DT link for backward compatibility

Signed-off-by: Igor Pecovnik <igor.pecovnik@gmail.com>
---
arch/arm64/boot/dts/rockchip/Makefile | 1 +
arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts | 1 +
2 files changed, 2 insertions(+)
create mode 120000 arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts

diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index e91faee4a..8fb764404 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -38,6 +38,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-pinebook-pro.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-puma-haikou.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-roc-pc.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-roc-pc-mezzanine.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rock-pi-4.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rock-pi-4a.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rock-pi-4b.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rock-pi-4c.dtb
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts
new file mode 120000
index 000000000..07e8a11b0
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts
@@ -0,0 +1 @@
+rk3399-rock-pi-4b.dts
\ No newline at end of file
--
Created with Armbian build tools https://github.com/armbian/build


+ 1
- 0
pkgs/top-level/all-packages.nix View File

@@ -18438,6 +18438,7 @@ in
ubootClearfog
ubootGuruplug
ubootJetsonTK1
ubootNanoPiM4v2
ubootNovena
ubootOdroidC2
ubootOdroidXU3


Loading…
Cancel
Save