diff --git a/config/kernel/linux-rk35xx-edge.config b/config/kernel/linux-rk35xx-edge.config index dc7fbf5600..fb6b009442 100644 --- a/config/kernel/linux-rk35xx-edge.config +++ b/config/kernel/linux-rk35xx-edge.config @@ -3352,7 +3352,7 @@ CONFIG_RTL8XXXU=m # CONFIG_RTL8XXXU_UNTESTED is not set CONFIG_RTW88=m # CONFIG_RTW88_8822BE is not set -# CONFIG_RTW88_8822CE is not set +CONFIG_RTW88_8822CE=y # CONFIG_RTW88_8723DE is not set # CONFIG_RTW88_8821CE is not set # CONFIG_RTW89 is not set @@ -3377,16 +3377,16 @@ CONFIG_WLCORE_SPI=m CONFIG_WLCORE_SDIO=m CONFIG_WILINK_PLATFORM_DATA=y # CONFIG_RTL8822BS is not set -CONFIG_RTL8723DU=m -CONFIG_RTL8723DS=m -CONFIG_RTL8822CS=m -CONFIG_RTL8822BU=m +#CONFIG_RTL8723DU=m +#CONFIG_RTL8723DS=m +#CONFIG_RTL8822CS=m +#CONFIG_RTL8822BU=m CONFIG_RTL8188EU=m -CONFIG_RTL8821CU=m -CONFIG_88XXAU=m +#CONFIG_RTL8821CU=m +#CONFIG_88XXAU=m # CONFIG_RTL8192EU is not set -CONFIG_RTL8189FS=m -CONFIG_RTL8189ES=m +#CONFIG_RTL8189FS=m +#CONFIG_RTL8189ES=m CONFIG_WLAN_VENDOR_ZYDAS=y CONFIG_USB_ZD1201=m CONFIG_ZD1211RW=m diff --git a/config/sources/families/rk35xx.conf b/config/sources/families/rk35xx.conf index 23bc2fa84a..8c88a2d2ee 100644 --- a/config/sources/families/rk35xx.conf +++ b/config/sources/families/rk35xx.conf @@ -21,7 +21,7 @@ case $BRANCH in # temporary until kernel 5.16 is well supported for rockchip64 # it has to be its own family too edge) - KERNELBRANCH="branch:linux-5.16.y" + KERNELBRANCH="branch:linux-5.17.y" KERNELPATCHDIR='rk35xx-'$BRANCH SKIP_BOOTSPLASH="yes" LINUXFAMILY=rk35xx diff --git a/patch/kernel/archive/rk35xx-5.16/rk3568-dts-pcie-support.patch b/patch/kernel/archive/rk35xx-5.16/rk3568-dts-pcie-support.patch new file mode 100644 index 0000000000..5672e46a97 --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.16/rk3568-dts-pcie-support.patch @@ -0,0 +1,104 @@ +diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi +index d91df1cde736..75fbf7992a6a 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi +@@ -23,6 +23,76 @@ qos_sata0: qos@fe190200 { + reg = <0x0 0xfe190200 0x0 0x20>; + }; + ++ pcie3x2: pcie@fe280000 { ++ compatible = "rockchip,rk3568-pcie", "snps,dw-pcie"; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ bus-range = <0x20 0x2f>; ++ clocks = <&cru ACLK_PCIE30X2_MST>, <&cru ACLK_PCIE30X2_SLV>, ++ <&cru ACLK_PCIE30X2_DBI>, <&cru PCLK_PCIE30X2>, ++ <&cru CLK_PCIE30X2_AUX_NDFT>; ++ clock-names = "aclk_mst", "aclk_slv", ++ "aclk_dbi", "pclk", "aux"; ++ device_type = "pci"; ++ interrupts = , ++ , ++ , ++ , ++ ; ++ interrupt-names = "sys", "pmc", "msg", "legacy", "err"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie3x2_intc 0>, ++ <0 0 0 2 &pcie3x2_intc 1>, ++ <0 0 0 3 &pcie3x2_intc 2>, ++ <0 0 0 4 &pcie3x2_intc 3>; ++ linux,pci-domain = <2>; ++ num-ib-windows = <6>; ++ num-ob-windows = <2>; ++ max-link-speed = <3>; ++ msi-map = <0x2000 &its 0x2000 0x1000>; ++ num-lanes = <2>; ++ phys = <&pcie30phy>; ++ phy-names = "pcie-phy"; ++ power-domains = <&power RK3568_PD_PIPE>; ++ ranges = <0x00000800 0x0 0x80000000 0x3 0x80000000 0x0 0x800000 ++ 0x81000000 0x0 0x80800000 0x3 0x80800000 0x0 0x100000 ++ 0x83000000 0x0 0x80900000 0x3 0x80900000 0x0 0x3f700000>; ++ reg = <0x3 0xc0800000 0x0 0x400000>, ++ <0x0 0xfe280000 0x0 0x10000>; ++ reg-names = "pcie-dbi", "pcie-apb"; ++ resets = <&cru SRST_PCIE30X2_POWERUP>; ++ reset-names = "pipe"; ++ /* rockchip,bifurcation; lane0 when using 1+1 */ ++ status = "disabled"; ++ ++ pcie3x2_intc: legacy-interrupt-controller { ++ interrupt-controller; ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-parent = <&gic>; ++ interrupts = ; ++ }; ++ }; ++ ++ pcie30phy: phy@fe8c0000 { ++ compatible = "rockchip,rk3568-pcie3-phy"; ++ reg = <0x0 0xfe8c0000 0x0 0x20000>; ++ #phy-cells = <0>; ++ clocks = <&pmucru CLK_PCIE30PHY_REF_M>, <&pmucru CLK_PCIE30PHY_REF_N>, ++ <&cru PCLK_PCIE30PHY>; ++ clock-names = "refclk_m", "refclk_n", "pclk"; ++ resets = <&cru SRST_PCIE30PHY>; ++ reset-names = "phy"; ++ rockchip,phy-grf = <&pcie30_phy_grf>; ++ status = "disabled"; ++ }; ++ ++ pcie30_phy_grf: syscon@fdcb8000 { ++ compatible = "rockchip,pcie30-phy-grf", "syscon"; ++ reg = <0x0 0xfdcb8000 0x0 0x10000>; ++ }; ++ + gmac0: ethernet@fe2a0000 { + compatible = "rockchip,rk3568-gmac", "snps,dwmac-4.20a"; + reg = <0x0 0xfe2a0000 0x0 0x10000>; +diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +index 688e3585525a..5274a34c0fbf 100644 +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -199,9 +199,18 @@ gic: interrupt-controller@fd400000 { + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; + mbi-alias = <0x0 0xfd410000>; + mbi-ranges = <296 24>; + msi-controller; ++ its: interrupt-controller@fd440000 { ++ compatible = "arm,gic-v3-its"; ++ msi-controller; ++ #msi-cells = <1>; ++ reg = <0x0 0xfd440000 0x0 0x20000>; ++ }; + }; + + pmugrf: syscon@fdc20000 { diff --git a/patch/kernel/archive/rk35xx-5.16/rk3568-dts-radxa-rock3a-support.patch b/patch/kernel/archive/rk35xx-5.16/rk3568-dts-radxa-rock3a-support.patch index 707b033a33..db24f9520c 100644 --- a/patch/kernel/archive/rk35xx-5.16/rk3568-dts-radxa-rock3a-support.patch +++ b/patch/kernel/archive/rk35xx-5.16/rk3568-dts-radxa-rock3a-support.patch @@ -9,10 +9,10 @@ index 479906f3ad7b..bf2a58e3a871 100644 +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-rock-3-a.dtb diff --git a/arch/arm64/boot/dts/rockchip/rk3568-rock-3-a.dts b/arch/arm64/boot/dts/rockchip/rk3568-rock-3-a.dts new file mode 100644 -index 000000000000..04da5128dff6 +index 000000000000..9e9124dc6c59 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-rock-3-a.dts -@@ -0,0 +1,651 @@ +@@ -0,0 +1,809 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2021 Rockchip Electronics Co., Ltd. @@ -70,6 +70,36 @@ index 000000000000..04da5128dff6 + pinctrl-0 = <&user_led_enable_h>; + retain-state-suspended; + }; ++ ++ wifi-led { ++ gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "rfkill2"; ++ default-state = "on"; ++ pinctrl-0 = <&wifi_led>; ++ }; ++ }; ++ ++ rk809_sound: rk809-sound { ++ status = "okay"; ++ compatible = "simple-audio-card"; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,name = "rockchip,rk809-codec"; ++ simple-audio-card,mclk-fs = <256>; ++ ++ simple-audio-card,cpu { ++ sound-dai = <&i2s1_8ch>; ++ }; ++ simple-audio-card,codec { ++ sound-dai = <&rk809_codec>; ++ }; ++ }; ++ ++ rk_headset: rk-headset { ++ compatible = "rockchip_headset"; ++ headset_gpio = <&gpio2 RK_PD2 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hp_det>; ++ io-channels = <&saradc 2>; //HP_HOOK pin + }; + + sdio_pwrseq: sdio-pwrseq { @@ -262,6 +292,7 @@ index 000000000000..04da5128dff6 + compatible = "tcs,tcs4525"; + reg = <0x1c>; + fcs,suspend-voltage-selector = <1>; ++ regulator-compatible = "fan53555-reg"; + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1150000>; @@ -282,9 +313,21 @@ index 000000000000..04da5128dff6 + interrupt-parent = <&gpio0>; + interrupts = ; + #clock-cells = <1>; -+ pinctrl-names = "default"; ++ pinctrl-names = "default", "pmic-sleep", ++ "pmic-power-off", "pmic-reset"; + pinctrl-0 = <&pmic_int>; ++ pinctrl-1 = <&soc_slppin_slp>, <&rk817_slppin_slp>; ++ pinctrl-2 = <&soc_slppin_gpio>, <&rk817_slppin_pwrdn>; ++ pinctrl-3 = <&soc_slppin_gpio>, <&rk817_slppin_rst>; ++ + rockchip,system-power-controller; ++ clock-output-names = "rk808-clkout1", "rk808-clkout2"; ++ //fb-inner-reg-idxs = <2>; ++ /* 1: rst regs (default in codes), 0: rst the pmic */ ++ pmic-reset-func = <0>; ++ /* not save the PMIC_POWER_EN register in uboot */ ++ not-save-power-en = <1>; ++ + vcc1-supply = <&vcc3v3_sys>; + vcc2-supply = <&vcc3v3_sys>; + vcc3-supply = <&vcc3v3_sys>; @@ -296,6 +339,35 @@ index 000000000000..04da5128dff6 + vcc9-supply = <&vcc3v3_sys>; + wakeup-source; + ++ pwrkey { ++ status = "okay"; ++ }; ++ ++ pinctrl_rk8xx: pinctrl_rk8xx { ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ rk817_slppin_null: rk817_slppin_null { ++ pins = "gpio_slp"; ++ function = "pin_fun0"; ++ }; ++ ++ rk817_slppin_slp: rk817_slppin_slp { ++ pins = "gpio_slp"; ++ function = "pin_fun1"; ++ }; ++ ++ rk817_slppin_pwrdn: rk817_slppin_pwrdn { ++ pins = "gpio_slp"; ++ function = "pin_fun2"; ++ }; ++ ++ rk817_slppin_rst: rk817_slppin_rst { ++ pins = "gpio_slp"; ++ function = "pin_fun3"; ++ }; ++ }; ++ + regulators { + vdd_logic: DCDC_REG1 { + regulator-name = "vdd_logic"; @@ -316,6 +388,8 @@ index 000000000000..04da5128dff6 + regulator-name = "vdd_gpu"; + regulator-init-microvolt = <900000>; + regulator-initial-mode = <0x2>; ++ regulator-always-on; ++ regulator-boot-on; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <6001>; @@ -338,6 +412,8 @@ index 000000000000..04da5128dff6 + + vdd_npu: DCDC_REG4 { + regulator-name = "vdd_npu"; ++ regulator-always-on; ++ regulator-boot-on; + regulator-init-microvolt = <900000>; + regulator-initial-mode = <0x2>; + regulator-min-microvolt = <500000>; @@ -363,6 +439,8 @@ index 000000000000..04da5128dff6 + + vdda0v9_image: LDO_REG1 { + regulator-name = "vdda0v9_image"; ++ regulator-always-on; ++ regulator-boot-on; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + @@ -398,6 +476,8 @@ index 000000000000..04da5128dff6 + + vccio_acodec: LDO_REG4 { + regulator-name = "vccio_acodec"; ++ regulator-always-on; ++ regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + @@ -408,6 +488,8 @@ index 000000000000..04da5128dff6 + + vccio_sd: LDO_REG5 { + regulator-name = "vccio_sd"; ++ regulator-always-on; ++ regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + @@ -456,6 +538,8 @@ index 000000000000..04da5128dff6 + + vcca1v8_image: LDO_REG9 { + regulator-name = "vcca1v8_image"; ++ regulator-always-on; ++ regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + @@ -476,12 +560,29 @@ index 000000000000..04da5128dff6 + + vcc3v3_sd: SWITCH_REG2 { + regulator-name = "vcc3v3_sd"; ++ regulator-always-on; ++ regulator-boot-on; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + }; ++ ++ rk809_codec: codec { ++ #sound-dai-cells = <0>; ++ compatible = "rockchip,rk809-codec", "rockchip,rk817-codec"; ++ clocks = <&cru I2S1_MCLKOUT_TX>; ++ clock-names = "mclk"; ++ assigned-clocks = <&cru I2S1_MCLKOUT_TX>; ++ assigned-clock-parents = <&cru CLK_I2S1_8CH_TX>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s1m0_mclk>; ++ hp-volume = <20>; ++ spk-volume = <3>; ++ mic-in-differential; ++ status = "okay"; ++ }; + }; +}; + @@ -498,6 +599,10 @@ index 000000000000..04da5128dff6 + user_led_enable_h: user-led-enable-h { + rockchip,pins = <0 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; + }; ++ ++ wifi_led: wifi-led { ++ rockchip,pins = <2 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; + }; + + sdio-pwrseq { @@ -523,6 +628,39 @@ index 000000000000..04da5128dff6 + rockchip,pins = + <0 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>; + }; ++ ++ soc_slppin_gpio: soc_slppin_gpio { ++ rockchip,pins = ++ <0 RK_PA2 RK_FUNC_GPIO &pcfg_output_low>; ++ }; ++ ++ soc_slppin_slp: soc_slppin_slp { ++ rockchip,pins = ++ <0 RK_PA2 1 &pcfg_pull_none>; ++ }; ++ ++ soc_slppin_rst: soc_slppin_rst { ++ rockchip,pins = ++ <0 RK_PA2 2 &pcfg_pull_none>; ++ }; ++ }; ++ ++ wireless-wlan { ++ wifi_host_wake_irq: wifi-host-wake-irq { ++ rockchip,pins = <3 RK_PD5 RK_FUNC_GPIO &pcfg_pull_down>; ++ }; ++ }; ++ ++ wireless-bluetooth { ++ uart1_gpios: uart1-gpios { ++ rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ headphone { ++ hp_det: hp-det { ++ rockchip,pins = <2 RK_PD2 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; + }; +}; + @@ -534,7 +672,6 @@ index 000000000000..04da5128dff6 + pmuio1-supply = <&vcc3v3_pmu>; + pmuio2-supply = <&vcc3v3_pmu>; + vccio1-supply = <&vccio_acodec>; -+ vccio2-supply = <&vcc_1v8>; + vccio3-supply = <&vccio_sd>; + vccio4-supply = <&vcc_1v8>; + vccio5-supply = <&vcc_3v3>; @@ -664,3 +801,24 @@ index 000000000000..04da5128dff6 +&usb_host1_ohci { + status = "okay"; +}; ++ ++&pcie30phy { ++ status = "okay"; ++}; ++ ++&pcie3x2 { ++ reset-gpios = <&gpio2 RK_PD6 GPIO_ACTIVE_HIGH>; ++ vpcie3v3-supply = <&pcie30_3v3>; ++ //num-lanes = <2>; ++ pinctrl-0 = <&pcie30x2m1_pins>; ++ bus-scan-delay-ms = <1000>; ++ status = "okay"; ++}; ++ ++&pcie2x1 { ++ reset-gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_HIGH>; ++ vpcie3v3-supply = <&pcie30_3v3>; ++ pinctrl-0 = <&pcie20m1_pins>; ++ bus-scan-delay-ms = <1000>; ++ status = "okay"; ++}; diff --git a/patch/kernel/archive/rk35xx-5.16/rk3568-pwm-rockchip.patch b/patch/kernel/archive/rk35xx-5.16/rk3568-pwm-rockchip.patch new file mode 100644 index 0000000000..9a4f15d238 --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.16/rk3568-pwm-rockchip.patch @@ -0,0 +1,428 @@ +diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c +index f3647b317152..7905deaf7055 100644 +--- a/drivers/pwm/pwm-rockchip.c ++++ b/drivers/pwm/pwm-rockchip.c +@@ -1,9 +1,12 @@ +-// SPDX-License-Identifier: GPL-2.0-only + /* + * PWM driver for Rockchip SoCs + * + * Copyright (C) 2014 Beniamino Galvani + * Copyright (C) 2014 ROCKCHIP, Inc. ++ * ++ * 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 +@@ -11,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -26,15 +30,25 @@ + #define PWM_INACTIVE_POSITIVE (1 << 4) + #define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE) + #define PWM_OUTPUT_LEFT (0 << 5) ++#define PWM_OUTPUT_CENTER (1 << 5) + #define PWM_LOCK_EN (1 << 6) + #define PWM_LP_DISABLE (0 << 8) + ++#define PWM_ONESHOT_COUNT_SHIFT 24 ++#define PWM_ONESHOT_COUNT_MAX 256 ++ + struct rockchip_pwm_chip { + struct pwm_chip chip; + struct clk *clk; + struct clk *pclk; ++ struct pinctrl *pinctrl; ++ struct pinctrl_state *active_state; + const struct rockchip_pwm_data *data; + void __iomem *base; ++ unsigned long clk_rate; ++ bool vop_pwm_en; /* indicate voppwm mirror register state */ ++ bool center_aligned; ++ bool oneshot; + }; + + struct rockchip_pwm_regs { +@@ -49,7 +63,9 @@ struct rockchip_pwm_data { + unsigned int prescaler; + bool supports_polarity; + bool supports_lock; ++ bool vop_pwm; + u32 enable_conf; ++ u32 enable_conf_mask; + }; + + static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) +@@ -63,7 +79,6 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, + { + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u32 enable_conf = pc->data->enable_conf; +- unsigned long clk_rate; + u64 tmp; + u32 val; + int ret; +@@ -72,59 +87,77 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, + if (ret) + return; + +- ret = clk_enable(pc->clk); +- if (ret) +- return; +- +- clk_rate = clk_get_rate(pc->clk); +- + tmp = readl_relaxed(pc->base + pc->data->regs.period); + tmp *= pc->data->prescaler * NSEC_PER_SEC; +- state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); ++ state->period = DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate); + + tmp = readl_relaxed(pc->base + pc->data->regs.duty); + tmp *= pc->data->prescaler * NSEC_PER_SEC; +- state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); ++ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate); + + val = readl_relaxed(pc->base + pc->data->regs.ctrl); +- state->enabled = (val & enable_conf) == enable_conf; +- +- if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE)) +- state->polarity = PWM_POLARITY_INVERSED; ++ if (pc->data->supports_polarity) ++ state->enabled = ((val & enable_conf) != enable_conf) ? ++ false : true; + else +- state->polarity = PWM_POLARITY_NORMAL; ++ state->enabled = ((val & enable_conf) == enable_conf) ? ++ true : false; ++ ++ if (pc->data->supports_polarity) { ++ if (!(val & PWM_DUTY_POSITIVE)) ++ state->polarity = PWM_POLARITY_INVERSED; ++ } + +- clk_disable(pc->clk); + clk_disable(pc->pclk); + } + + static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, +- const struct pwm_state *state) ++ struct pwm_state *state) + { + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + unsigned long period, duty; +- u64 clk_rate, div; ++ unsigned long flags; ++ u64 div; + u32 ctrl; + +- clk_rate = clk_get_rate(pc->clk); +- + /* + * Since period and duty cycle registers have a width of 32 + * bits, every possible input period can be obtained using the + * default prescaler value for all practical clock rate values. + */ +- div = clk_rate * state->period; ++ div = (u64)pc->clk_rate * state->period; + period = DIV_ROUND_CLOSEST_ULL(div, + pc->data->prescaler * NSEC_PER_SEC); + +- div = clk_rate * state->duty_cycle; ++ div = (u64)pc->clk_rate * state->duty_cycle; + duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); + ++ local_irq_save(flags); + /* + * Lock the period and duty of previous configuration, then + * change the duty and period, that would not be effective. + */ + ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); ++ if (pc->data->vop_pwm) { ++ if (pc->vop_pwm_en) ++ ctrl |= PWM_ENABLE; ++ else ++ ctrl &= ~PWM_ENABLE; ++ } ++ ++#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT ++ if (state->oneshot_count > PWM_ONESHOT_COUNT_MAX) { ++ pc->oneshot = false; ++ dev_err(chip->dev, "Oneshot_count value overflow.\n"); ++ } else if (state->oneshot_count > 0) { ++ pc->oneshot = true; ++ ctrl |= (state->oneshot_count - 1) << PWM_ONESHOT_COUNT_SHIFT; ++ } else { ++ pc->oneshot = false; ++ ctrl |= PWM_CONTINUOUS; ++ } ++#endif ++ + if (pc->data->supports_lock) { + ctrl |= PWM_LOCK_EN; + writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl); +@@ -150,6 +183,7 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + ctrl &= ~PWM_LOCK_EN; + + writel(ctrl, pc->base + pc->data->regs.ctrl); ++ local_irq_restore(flags); + } + + static int rockchip_pwm_enable(struct pwm_chip *chip, +@@ -168,13 +202,24 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, + } + + val = readl_relaxed(pc->base + pc->data->regs.ctrl); ++ val &= ~pc->data->enable_conf_mask; ++ ++ if (PWM_OUTPUT_CENTER & pc->data->enable_conf_mask) { ++ if (pc->center_aligned) ++ val |= PWM_OUTPUT_CENTER; ++ } + +- if (enable) ++ if (enable) { + val |= enable_conf; +- else ++ if (pc->oneshot) ++ val &= ~PWM_CONTINUOUS; ++ } else { + val &= ~enable_conf; ++ } + + writel_relaxed(val, pc->base + pc->data->regs.ctrl); ++ if (pc->data->vop_pwm) ++ pc->vop_pwm_en = enable; + + if (!enable) + clk_disable(pc->clk); +@@ -194,10 +239,6 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + if (ret) + return ret; + +- ret = clk_enable(pc->clk); +- if (ret) +- return ret; +- + pwm_get_state(pwm, &curstate); + enabled = curstate.enabled; + +@@ -216,8 +257,15 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + goto out; + } + ++ /* ++ * Update the state with the real hardware, which can differ a bit ++ * because of period/duty_cycle approximation. ++ */ ++ rockchip_pwm_get_state(chip, pwm, state); ++ ++ if (state->enabled || pc->oneshot) ++ ret = pinctrl_select_state(pc->pinctrl, pc->active_state); + out: +- clk_disable(pc->clk); + clk_disable(pc->pclk); + + return ret; +@@ -239,7 +287,9 @@ static const struct rockchip_pwm_data pwm_data_v1 = { + .prescaler = 2, + .supports_polarity = false, + .supports_lock = false, ++ .vop_pwm = false, + .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN, ++ .enable_conf_mask = BIT(1) | BIT(3), + }; + + static const struct rockchip_pwm_data pwm_data_v2 = { +@@ -252,8 +302,10 @@ static const struct rockchip_pwm_data pwm_data_v2 = { + .prescaler = 1, + .supports_polarity = true, + .supports_lock = false, ++ .vop_pwm = false, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, ++ .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8), + }; + + static const struct rockchip_pwm_data pwm_data_vop = { +@@ -266,8 +318,10 @@ static const struct rockchip_pwm_data pwm_data_vop = { + .prescaler = 1, + .supports_polarity = true, + .supports_lock = false, ++ .vop_pwm = true, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, ++ .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8), + }; + + static const struct rockchip_pwm_data pwm_data_v3 = { +@@ -280,8 +334,10 @@ static const struct rockchip_pwm_data pwm_data_v3 = { + .prescaler = 1, + .supports_polarity = true, + .supports_lock = true, ++ .vop_pwm = false, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, ++ .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8), + }; + + static const struct of_device_id rockchip_pwm_dt_ids[] = { +@@ -297,8 +353,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + { + const struct of_device_id *id; + struct rockchip_pwm_chip *pc; +- u32 enable_conf, ctrl; +- bool enabled; ++ struct resource *r; + int ret, count; + + id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); +@@ -309,16 +364,22 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + if (!pc) + return -ENOMEM; + +- pc->base = devm_platform_ioremap_resource(pdev, 0); ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ pc->base = devm_ioremap(&pdev->dev, r->start, ++ resource_size(r)); + if (IS_ERR(pc->base)) + return PTR_ERR(pc->base); + + pc->clk = devm_clk_get(&pdev->dev, "pwm"); + if (IS_ERR(pc->clk)) { + pc->clk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(pc->clk)) +- return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk), +- "Can't get PWM clk\n"); ++ if (IS_ERR(pc->clk)) { ++ ret = PTR_ERR(pc->clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "Can't get bus clk: %d\n", ++ ret); ++ return ret; ++ } + } + + count = of_count_phandle_with_args(pdev->dev.of_node, +@@ -337,26 +398,44 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + + ret = clk_prepare_enable(pc->clk); + if (ret) { +- dev_err(&pdev->dev, "Can't prepare enable PWM clk: %d\n", ret); ++ dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret); + return ret; + } + +- ret = clk_prepare_enable(pc->pclk); ++ ret = clk_prepare(pc->pclk); + if (ret) { +- dev_err(&pdev->dev, "Can't prepare enable APB clk: %d\n", ret); ++ dev_err(&pdev->dev, "Can't prepare APB clk: %d\n", ret); + goto err_clk; + } + ++ pc->pinctrl = devm_pinctrl_get(&pdev->dev); ++ if (IS_ERR(pc->pinctrl)) { ++ dev_err(&pdev->dev, "Get pinctrl failed!\n"); ++ return PTR_ERR(pc->pinctrl); ++ } ++ ++ pc->active_state = pinctrl_lookup_state(pc->pinctrl, "active"); ++ if (IS_ERR(pc->active_state)) { ++ dev_err(&pdev->dev, "No active pinctrl state\n"); ++ return PTR_ERR(pc->active_state); ++ } ++ + platform_set_drvdata(pdev, pc); + + pc->data = id->data; + pc->chip.dev = &pdev->dev; + pc->chip.ops = &rockchip_pwm_ops; ++ pc->chip.base = -1; + pc->chip.npwm = 1; ++ pc->clk_rate = clk_get_rate(pc->clk); + +- enable_conf = pc->data->enable_conf; +- ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); +- enabled = (ctrl & enable_conf) == enable_conf; ++ if (pc->data->supports_polarity) { ++ pc->chip.of_xlate = of_pwm_xlate_with_flags; ++ pc->chip.of_pwm_n_cells = 3; ++ } ++ ++ pc->center_aligned = ++ device_property_read_bool(&pdev->dev, "center-aligned"); + + ret = pwmchip_add(&pc->chip); + if (ret < 0) { +@@ -365,15 +444,13 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + } + + /* Keep the PWM clk enabled if the PWM appears to be up and running. */ +- if (!enabled) ++ if (!pwm_is_enabled(pc->chip.pwms)) + clk_disable(pc->clk); + +- clk_disable(pc->pclk); +- + return 0; + + err_pclk: +- clk_disable_unprepare(pc->pclk); ++ clk_unprepare(pc->pclk); + err_clk: + clk_disable_unprepare(pc->clk); + +@@ -384,11 +461,24 @@ static int rockchip_pwm_remove(struct platform_device *pdev) + { + struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev); + +- pwmchip_remove(&pc->chip); ++ /* ++ * Disable the PWM clk before unpreparing it if the PWM device is still ++ * running. This should only happen when the last PWM user left it ++ * enabled, or when nobody requested a PWM that was previously enabled ++ * by the bootloader. ++ * ++ * FIXME: Maybe the core should disable all PWM devices in ++ * pwmchip_remove(). In this case we'd only have to call ++ * clk_unprepare() after pwmchip_remove(). ++ * ++ */ ++ if (pwm_is_enabled(pc->chip.pwms)) ++ clk_disable(pc->clk); + + clk_unprepare(pc->pclk); + clk_unprepare(pc->clk); + ++ pwmchip_remove(&pc->chip); + return 0; + } + +@@ -400,7 +490,21 @@ static struct platform_driver rockchip_pwm_driver = { + .probe = rockchip_pwm_probe, + .remove = rockchip_pwm_remove, + }; ++#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT ++static int __init rockchip_pwm_driver_init(void) ++{ ++ return platform_driver_register(&rockchip_pwm_driver); ++} ++subsys_initcall(rockchip_pwm_driver_init); ++ ++static void __exit rockchip_pwm_driver_exit(void) ++{ ++ platform_driver_unregister(&rockchip_pwm_driver); ++} ++module_exit(rockchip_pwm_driver_exit); ++#else + module_platform_driver(rockchip_pwm_driver); ++#endif + + MODULE_AUTHOR("Beniamino Galvani "); + MODULE_DESCRIPTION("Rockchip SoC PWM driver"); diff --git a/patch/kernel/archive/rk35xx-5.16/rk356x-add-dtb-overlay-support.patch b/patch/kernel/archive/rk35xx-5.16/rk356x-add-dtb-overlay-support.patch new file mode 100644 index 0000000000..c2fe8e29ea --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.16/rk356x-add-dtb-overlay-support.patch @@ -0,0 +1,12 @@ +diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib +index d1f865b8c0cb..998845187686 100644 +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -295,6 +295,7 @@ quiet_cmd_gzip = GZIP $@ + # --------------------------------------------------------------------------- + DTC ?= $(objtree)/scripts/dtc/dtc + DTC_FLAGS += -Wno-interrupt_provider ++DTC_FLAGS += -@ + + # Disable noisy checks by default + ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),) diff --git a/patch/kernel/archive/rk35xx-5.17/0001-Revert-net-Remove-net-ipx.h-and-uapi-linux-ipx.h-hea.patch b/patch/kernel/archive/rk35xx-5.17/0001-Revert-net-Remove-net-ipx.h-and-uapi-linux-ipx.h-hea.patch new file mode 100644 index 0000000000..8fe8cd6434 --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/0001-Revert-net-Remove-net-ipx.h-and-uapi-linux-ipx.h-hea.patch @@ -0,0 +1,287 @@ +From 3ec70749ae3cb072f19d886981a217121f776415 Mon Sep 17 00:00:00 2001 +From: Igor Pecovnik +Date: Sat, 6 Nov 2021 19:15:23 +0100 +Subject: [PATCH] Revert "net: Remove net/ipx.h and uapi/linux/ipx.h header + files" + +This reverts commit 6c9b40844751ea30c72f7a2f92f4d704bc6b2927. +--- + include/net/ipx.h | 171 +++++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ipx.h | 87 ++++++++++++++++++++ + 2 files changed, 258 insertions(+) + create mode 100644 include/net/ipx.h + create mode 100644 include/uapi/linux/ipx.h + +diff --git a/include/net/ipx.h b/include/net/ipx.h +new file mode 100644 +index 000000000000..9d1342807b59 +--- /dev/null ++++ b/include/net/ipx.h +@@ -0,0 +1,171 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef _NET_INET_IPX_H_ ++#define _NET_INET_IPX_H_ ++/* ++ * The following information is in its entirety obtained from: ++ * ++ * Novell 'IPX Router Specification' Version 1.10 ++ * Part No. 107-000029-001 ++ * ++ * Which is available from ftp.novell.com ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct ipx_address { ++ __be32 net; ++ __u8 node[IPX_NODE_LEN]; ++ __be16 sock; ++}; ++ ++#define ipx_broadcast_node "\377\377\377\377\377\377" ++#define ipx_this_node "\0\0\0\0\0\0" ++ ++#define IPX_MAX_PPROP_HOPS 8 ++ ++struct ipxhdr { ++ __be16 ipx_checksum __packed; ++#define IPX_NO_CHECKSUM cpu_to_be16(0xFFFF) ++ __be16 ipx_pktsize __packed; ++ __u8 ipx_tctrl; ++ __u8 ipx_type; ++#define IPX_TYPE_UNKNOWN 0x00 ++#define IPX_TYPE_RIP 0x01 /* may also be 0 */ ++#define IPX_TYPE_SAP 0x04 /* may also be 0 */ ++#define IPX_TYPE_SPX 0x05 /* SPX protocol */ ++#define IPX_TYPE_NCP 0x11 /* $lots for docs on this (SPIT) */ ++#define IPX_TYPE_PPROP 0x14 /* complicated flood fill brdcast */ ++ struct ipx_address ipx_dest __packed; ++ struct ipx_address ipx_source __packed; ++}; ++ ++/* From af_ipx.c */ ++extern int sysctl_ipx_pprop_broadcasting; ++ ++struct ipx_interface { ++ /* IPX address */ ++ __be32 if_netnum; ++ unsigned char if_node[IPX_NODE_LEN]; ++ refcount_t refcnt; ++ ++ /* physical device info */ ++ struct net_device *if_dev; ++ struct datalink_proto *if_dlink; ++ __be16 if_dlink_type; ++ ++ /* socket support */ ++ unsigned short if_sknum; ++ struct hlist_head if_sklist; ++ spinlock_t if_sklist_lock; ++ ++ /* administrative overhead */ ++ int if_ipx_offset; ++ unsigned char if_internal; ++ unsigned char if_primary; ++ ++ struct list_head node; /* node in ipx_interfaces list */ ++}; ++ ++struct ipx_route { ++ __be32 ir_net; ++ struct ipx_interface *ir_intrfc; ++ unsigned char ir_routed; ++ unsigned char ir_router_node[IPX_NODE_LEN]; ++ struct list_head node; /* node in ipx_routes list */ ++ refcount_t refcnt; ++}; ++ ++struct ipx_cb { ++ u8 ipx_tctrl; ++ __be32 ipx_dest_net; ++ __be32 ipx_source_net; ++ struct { ++ __be32 netnum; ++ int index; ++ } last_hop; ++}; ++ ++#include ++ ++struct ipx_sock { ++ /* struct sock has to be the first member of ipx_sock */ ++ struct sock sk; ++ struct ipx_address dest_addr; ++ struct ipx_interface *intrfc; ++ __be16 port; ++#ifdef CONFIG_IPX_INTERN ++ unsigned char node[IPX_NODE_LEN]; ++#endif ++ unsigned short type; ++ /* ++ * To handle special ncp connection-handling sockets for mars_nwe, ++ * the connection number must be stored in the socket. ++ */ ++ unsigned short ipx_ncp_conn; ++}; ++ ++static inline struct ipx_sock *ipx_sk(struct sock *sk) ++{ ++ return (struct ipx_sock *)sk; ++} ++ ++#define IPX_SKB_CB(__skb) ((struct ipx_cb *)&((__skb)->cb[0])) ++ ++#define IPX_MIN_EPHEMERAL_SOCKET 0x4000 ++#define IPX_MAX_EPHEMERAL_SOCKET 0x7fff ++ ++extern struct list_head ipx_routes; ++extern rwlock_t ipx_routes_lock; ++ ++extern struct list_head ipx_interfaces; ++struct ipx_interface *ipx_interfaces_head(void); ++extern spinlock_t ipx_interfaces_lock; ++ ++extern struct ipx_interface *ipx_primary_net; ++ ++int ipx_proc_init(void); ++void ipx_proc_exit(void); ++ ++const char *ipx_frame_name(__be16); ++const char *ipx_device_name(struct ipx_interface *intrfc); ++ ++static __inline__ void ipxitf_hold(struct ipx_interface *intrfc) ++{ ++ refcount_inc(&intrfc->refcnt); ++} ++ ++void ipxitf_down(struct ipx_interface *intrfc); ++struct ipx_interface *ipxitf_find_using_net(__be32 net); ++int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb, char *node); ++__be16 ipx_cksum(struct ipxhdr *packet, int length); ++int ipxrtr_add_route(__be32 network, struct ipx_interface *intrfc, ++ unsigned char *node); ++void ipxrtr_del_routes(struct ipx_interface *intrfc); ++int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, ++ struct msghdr *msg, size_t len, int noblock); ++int ipxrtr_route_skb(struct sk_buff *skb); ++struct ipx_route *ipxrtr_lookup(__be32 net); ++int ipxrtr_ioctl(unsigned int cmd, void __user *arg); ++ ++static __inline__ void ipxitf_put(struct ipx_interface *intrfc) ++{ ++ if (refcount_dec_and_test(&intrfc->refcnt)) ++ ipxitf_down(intrfc); ++} ++ ++static __inline__ void ipxrtr_hold(struct ipx_route *rt) ++{ ++ refcount_inc(&rt->refcnt); ++} ++ ++static __inline__ void ipxrtr_put(struct ipx_route *rt) ++{ ++ if (refcount_dec_and_test(&rt->refcnt)) ++ kfree(rt); ++} ++#endif /* _NET_INET_IPX_H_ */ +diff --git a/include/uapi/linux/ipx.h b/include/uapi/linux/ipx.h +new file mode 100644 +index 000000000000..3168137adae8 +--- /dev/null ++++ b/include/uapi/linux/ipx.h +@@ -0,0 +1,87 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++#ifndef _IPX_H_ ++#define _IPX_H_ ++#include /* for compatibility with glibc netipx/ipx.h */ ++#include ++#include ++#include ++#define IPX_NODE_LEN 6 ++#define IPX_MTU 576 ++ ++#if __UAPI_DEF_SOCKADDR_IPX ++struct sockaddr_ipx { ++ __kernel_sa_family_t sipx_family; ++ __be16 sipx_port; ++ __be32 sipx_network; ++ unsigned char sipx_node[IPX_NODE_LEN]; ++ __u8 sipx_type; ++ unsigned char sipx_zero; /* 16 byte fill */ ++}; ++#endif /* __UAPI_DEF_SOCKADDR_IPX */ ++ ++/* ++ * So we can fit the extra info for SIOCSIFADDR into the address nicely ++ */ ++#define sipx_special sipx_port ++#define sipx_action sipx_zero ++#define IPX_DLTITF 0 ++#define IPX_CRTITF 1 ++ ++#if __UAPI_DEF_IPX_ROUTE_DEFINITION ++struct ipx_route_definition { ++ __be32 ipx_network; ++ __be32 ipx_router_network; ++ unsigned char ipx_router_node[IPX_NODE_LEN]; ++}; ++#endif /* __UAPI_DEF_IPX_ROUTE_DEFINITION */ ++ ++#if __UAPI_DEF_IPX_INTERFACE_DEFINITION ++struct ipx_interface_definition { ++ __be32 ipx_network; ++ unsigned char ipx_device[16]; ++ unsigned char ipx_dlink_type; ++#define IPX_FRAME_NONE 0 ++#define IPX_FRAME_SNAP 1 ++#define IPX_FRAME_8022 2 ++#define IPX_FRAME_ETHERII 3 ++#define IPX_FRAME_8023 4 ++#define IPX_FRAME_TR_8022 5 /* obsolete */ ++ unsigned char ipx_special; ++#define IPX_SPECIAL_NONE 0 ++#define IPX_PRIMARY 1 ++#define IPX_INTERNAL 2 ++ unsigned char ipx_node[IPX_NODE_LEN]; ++}; ++#endif /* __UAPI_DEF_IPX_INTERFACE_DEFINITION */ ++ ++#if __UAPI_DEF_IPX_CONFIG_DATA ++struct ipx_config_data { ++ unsigned char ipxcfg_auto_select_primary; ++ unsigned char ipxcfg_auto_create_interfaces; ++}; ++#endif /* __UAPI_DEF_IPX_CONFIG_DATA */ ++ ++/* ++ * OLD Route Definition for backward compatibility. ++ */ ++ ++#if __UAPI_DEF_IPX_ROUTE_DEF ++struct ipx_route_def { ++ __be32 ipx_network; ++ __be32 ipx_router_network; ++#define IPX_ROUTE_NO_ROUTER 0 ++ unsigned char ipx_router_node[IPX_NODE_LEN]; ++ unsigned char ipx_device[16]; ++ unsigned short ipx_flags; ++#define IPX_RT_SNAP 8 ++#define IPX_RT_8022 4 ++#define IPX_RT_BLUEBOOK 2 ++#define IPX_RT_ROUTED 1 ++}; ++#endif /* __UAPI_DEF_IPX_ROUTE_DEF */ ++ ++#define SIOCAIPXITFCRT (SIOCPROTOPRIVATE) ++#define SIOCAIPXPRISLT (SIOCPROTOPRIVATE + 1) ++#define SIOCIPXCFGDATA (SIOCPROTOPRIVATE + 2) ++#define SIOCIPXNCPCONN (SIOCPROTOPRIVATE + 3) ++#endif /* _IPX_H_ */ +-- +2.25.1 + diff --git a/patch/kernel/archive/rk35xx-5.17/rk3568-dts-pcie-support.patch b/patch/kernel/archive/rk35xx-5.17/rk3568-dts-pcie-support.patch new file mode 100644 index 0000000000..5672e46a97 --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk3568-dts-pcie-support.patch @@ -0,0 +1,104 @@ +diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi +index d91df1cde736..75fbf7992a6a 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi +@@ -23,6 +23,76 @@ qos_sata0: qos@fe190200 { + reg = <0x0 0xfe190200 0x0 0x20>; + }; + ++ pcie3x2: pcie@fe280000 { ++ compatible = "rockchip,rk3568-pcie", "snps,dw-pcie"; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ bus-range = <0x20 0x2f>; ++ clocks = <&cru ACLK_PCIE30X2_MST>, <&cru ACLK_PCIE30X2_SLV>, ++ <&cru ACLK_PCIE30X2_DBI>, <&cru PCLK_PCIE30X2>, ++ <&cru CLK_PCIE30X2_AUX_NDFT>; ++ clock-names = "aclk_mst", "aclk_slv", ++ "aclk_dbi", "pclk", "aux"; ++ device_type = "pci"; ++ interrupts = , ++ , ++ , ++ , ++ ; ++ interrupt-names = "sys", "pmc", "msg", "legacy", "err"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie3x2_intc 0>, ++ <0 0 0 2 &pcie3x2_intc 1>, ++ <0 0 0 3 &pcie3x2_intc 2>, ++ <0 0 0 4 &pcie3x2_intc 3>; ++ linux,pci-domain = <2>; ++ num-ib-windows = <6>; ++ num-ob-windows = <2>; ++ max-link-speed = <3>; ++ msi-map = <0x2000 &its 0x2000 0x1000>; ++ num-lanes = <2>; ++ phys = <&pcie30phy>; ++ phy-names = "pcie-phy"; ++ power-domains = <&power RK3568_PD_PIPE>; ++ ranges = <0x00000800 0x0 0x80000000 0x3 0x80000000 0x0 0x800000 ++ 0x81000000 0x0 0x80800000 0x3 0x80800000 0x0 0x100000 ++ 0x83000000 0x0 0x80900000 0x3 0x80900000 0x0 0x3f700000>; ++ reg = <0x3 0xc0800000 0x0 0x400000>, ++ <0x0 0xfe280000 0x0 0x10000>; ++ reg-names = "pcie-dbi", "pcie-apb"; ++ resets = <&cru SRST_PCIE30X2_POWERUP>; ++ reset-names = "pipe"; ++ /* rockchip,bifurcation; lane0 when using 1+1 */ ++ status = "disabled"; ++ ++ pcie3x2_intc: legacy-interrupt-controller { ++ interrupt-controller; ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-parent = <&gic>; ++ interrupts = ; ++ }; ++ }; ++ ++ pcie30phy: phy@fe8c0000 { ++ compatible = "rockchip,rk3568-pcie3-phy"; ++ reg = <0x0 0xfe8c0000 0x0 0x20000>; ++ #phy-cells = <0>; ++ clocks = <&pmucru CLK_PCIE30PHY_REF_M>, <&pmucru CLK_PCIE30PHY_REF_N>, ++ <&cru PCLK_PCIE30PHY>; ++ clock-names = "refclk_m", "refclk_n", "pclk"; ++ resets = <&cru SRST_PCIE30PHY>; ++ reset-names = "phy"; ++ rockchip,phy-grf = <&pcie30_phy_grf>; ++ status = "disabled"; ++ }; ++ ++ pcie30_phy_grf: syscon@fdcb8000 { ++ compatible = "rockchip,pcie30-phy-grf", "syscon"; ++ reg = <0x0 0xfdcb8000 0x0 0x10000>; ++ }; ++ + gmac0: ethernet@fe2a0000 { + compatible = "rockchip,rk3568-gmac", "snps,dwmac-4.20a"; + reg = <0x0 0xfe2a0000 0x0 0x10000>; +diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +index 688e3585525a..5274a34c0fbf 100644 +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -199,9 +199,18 @@ gic: interrupt-controller@fd400000 { + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; + mbi-alias = <0x0 0xfd410000>; + mbi-ranges = <296 24>; + msi-controller; ++ its: interrupt-controller@fd440000 { ++ compatible = "arm,gic-v3-its"; ++ msi-controller; ++ #msi-cells = <1>; ++ reg = <0x0 0xfd440000 0x0 0x20000>; ++ }; + }; + + pmugrf: syscon@fdc20000 { diff --git a/patch/kernel/archive/rk35xx-5.17/rk3568-dts-radxa-rock3a-support.patch b/patch/kernel/archive/rk35xx-5.17/rk3568-dts-radxa-rock3a-support.patch new file mode 100644 index 0000000000..db24f9520c --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk3568-dts-radxa-rock3a-support.patch @@ -0,0 +1,824 @@ +diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile +index 479906f3ad7b..bf2a58e3a871 100644 +--- a/arch/arm64/boot/dts/rockchip/Makefile ++++ b/arch/arm64/boot/dts/rockchip/Makefile +@@ -58,3 +58,4 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire-excavator.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399pro-rock-pi-n10.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-quartz64-a.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb1-v10.dtb ++dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-rock-3-a.dtb +diff --git a/arch/arm64/boot/dts/rockchip/rk3568-rock-3-a.dts b/arch/arm64/boot/dts/rockchip/rk3568-rock-3-a.dts +new file mode 100644 +index 000000000000..9e9124dc6c59 +--- /dev/null ++++ b/arch/arm64/boot/dts/rockchip/rk3568-rock-3-a.dts +@@ -0,0 +1,809 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright (c) 2021 Rockchip Electronics Co., Ltd. ++ * ++ */ ++ ++/dts-v1/; ++ ++#include ++#include ++#include ++#include "rk3568.dtsi" ++ ++/ { ++ model = "Radxa Rock3A"; ++ compatible = "radxa,rk3568-rock-3a", "rockchip,rk3568"; ++ ++ aliases { ++ ethernet1 = &gmac1; ++ mmc0 = &sdmmc0; ++ mmc1 = &sdhci; ++ }; ++ ++ chosen: chosen { ++ stdout-path = "serial2:1500000n8"; ++ }; ++ ++ gmac1_clkin: external-gmac1-clock { ++ compatible = "fixed-clock"; ++ clock-frequency = <125000000>; ++ clock-output-names = "gmac1_clkin"; ++ #clock-cells = <0>; ++ }; ++ ++ hdmi-con { ++ compatible = "hdmi-connector"; ++ type = "c"; ++ ++ port { ++ hdmi_con_in: endpoint { ++ remote-endpoint = <&hdmi_out_con>; ++ }; ++ }; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ led-user { ++ label = "user-led"; ++ default-state = "on"; ++ gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "heartbeat"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&user_led_enable_h>; ++ retain-state-suspended; ++ }; ++ ++ wifi-led { ++ gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "rfkill2"; ++ default-state = "on"; ++ pinctrl-0 = <&wifi_led>; ++ }; ++ }; ++ ++ rk809_sound: rk809-sound { ++ status = "okay"; ++ compatible = "simple-audio-card"; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,name = "rockchip,rk809-codec"; ++ simple-audio-card,mclk-fs = <256>; ++ ++ simple-audio-card,cpu { ++ sound-dai = <&i2s1_8ch>; ++ }; ++ simple-audio-card,codec { ++ sound-dai = <&rk809_codec>; ++ }; ++ }; ++ ++ rk_headset: rk-headset { ++ compatible = "rockchip_headset"; ++ headset_gpio = <&gpio2 RK_PD2 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hp_det>; ++ io-channels = <&saradc 2>; //HP_HOOK pin ++ }; ++ ++ sdio_pwrseq: sdio-pwrseq { ++ status = "okay"; ++ compatible = "mmc-pwrseq-simple"; ++ clocks = <&rk809 1>; ++ clock-names = "ext_clock"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&wifi_enable_h>; ++ reset-gpios = <&gpio3 RK_PD4 GPIO_ACTIVE_LOW>; ++ }; ++ ++ dc_12v: dc-12v { ++ compatible = "regulator-fixed"; ++ regulator-name = "dc_12v"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <12000000>; ++ regulator-max-microvolt = <12000000>; ++ }; ++ ++ vcc3v3_sys: vcc3v3-sys { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc3v3_sys"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ vin-supply = <&dc_12v>; ++ }; ++ ++ vcc5v0_sys: vcc5v0-sys { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc5v0_sys"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ vin-supply = <&dc_12v>; ++ }; ++ ++ vcc5v0_host: vcc5v0-host { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc5v0_host"; ++ enable-active-high; ++ gpio = <&gpio0 RK_PA6 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&vcc5v0_host_en>; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ vcc5v0_otg: vcc5v0-otg { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc5v0_otg"; ++ enable-active-high; ++ gpio = <&gpio0 RK_PA5 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&vcc5v0_otg_en>; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ vcc5v0_hub: vcc5v0-hub { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc5v0_hub"; ++ enable-active-high; ++ gpio = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&vcc5v0_hub_en>; ++ regulator-always-on; ++ }; ++ ++ pcie30_avdd0v9: pcie30-avdd0v9 { ++ compatible = "regulator-fixed"; ++ regulator-name = "pcie30_avdd0v9"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ vin-supply = <&vcc3v3_sys>; ++ }; ++ ++ pcie30_avdd1v8: pcie30-avdd1v8 { ++ compatible = "regulator-fixed"; ++ regulator-name = "pcie30_avdd1v8"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ vin-supply = <&vcc3v3_sys>; ++ }; ++ ++ pcie30_3v3: gpio-regulator { ++ compatible = "regulator-gpio"; ++ regulator-name = "pcie30_3v3"; ++ regulator-min-microvolt = <100000>; ++ regulator-max-microvolt = <3300000>; ++ gpios = <&gpio0 RK_PD4 GPIO_ACTIVE_HIGH>; ++ gpios-states = <0x1>; ++ states = <100000 0x0 ++ 3300000 0x1>; ++ }; ++ ++ vcc3v3_lcd0_n: vcc3v3-lcd0-n { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc3v3_lcd0_n"; ++ regulator-boot-on; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcc3v3_lcd1_n: vcc3v3-lcd1-n { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc3v3_lcd1_n"; ++ regulator-boot-on; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ ++}; ++ ++&combphy0 { ++ status = "okay"; ++}; ++ ++&combphy1 { ++ status = "okay"; ++}; ++ ++&combphy2 { ++ status = "okay"; ++}; ++ ++&gmac1 { ++ phy-mode = "rgmii"; ++ clock_in_out = "input"; ++ ++ snps,reset-gpio = <&gpio3 RK_PB0 GPIO_ACTIVE_LOW>; ++ snps,reset-active-low; ++ /* Reset time is 20ms, 100ms for rtl8211f */ ++ snps,reset-delays-us = <0 20000 100000>; ++ ++ assigned-clocks = <&cru SCLK_GMAC1_RX_TX>, <&cru SCLK_GMAC1>; ++ assigned-clock-parents = <&cru SCLK_GMAC1_RGMII_SPEED>, <&gmac1_clkin>; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gmac1m1_miim ++ &gmac1m1_tx_bus2 ++ &gmac1m1_rx_bus2 ++ &gmac1m1_rgmii_clk ++ &gmac1m1_rgmii_bus ++ &gmac1m1_clkinout>; ++ ++ tx_delay = <0x4f>; ++ rx_delay = <0x26>; ++ ++ phy-handle = <&rgmii_phy1>; ++ status = "okay"; ++}; ++ ++&hdmi { ++ avdd-0v9-supply = <&vdda0v9_image>; ++ avdd-1v8-supply = <&vcca1v8_image>; ++ status = "okay"; ++}; ++ ++&hdmi_in { ++ hdmi_in_vp0: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&vp0_out_hdmi>; ++ }; ++}; ++ ++&hdmi_out { ++ hdmi_out_con: endpoint { ++ remote-endpoint = <&hdmi_con_in>; ++ }; ++}; ++ ++&i2c0 { ++ status = "okay"; ++ ++ vdd_cpu: regulator@1c { ++ compatible = "tcs,tcs4525"; ++ reg = <0x1c>; ++ fcs,suspend-voltage-selector = <1>; ++ regulator-compatible = "fan53555-reg"; ++ regulator-name = "vdd_cpu"; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1150000>; ++ regulator-ramp-delay = <2300>; ++ regulator-always-on; ++ regulator-boot-on; ++ vin-supply = <&vcc5v0_sys>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ ++ rk809: pmic@20 { ++ compatible = "rockchip,rk809"; ++ reg = <0x20>; ++ interrupt-parent = <&gpio0>; ++ interrupts = ; ++ #clock-cells = <1>; ++ pinctrl-names = "default", "pmic-sleep", ++ "pmic-power-off", "pmic-reset"; ++ pinctrl-0 = <&pmic_int>; ++ pinctrl-1 = <&soc_slppin_slp>, <&rk817_slppin_slp>; ++ pinctrl-2 = <&soc_slppin_gpio>, <&rk817_slppin_pwrdn>; ++ pinctrl-3 = <&soc_slppin_gpio>, <&rk817_slppin_rst>; ++ ++ rockchip,system-power-controller; ++ clock-output-names = "rk808-clkout1", "rk808-clkout2"; ++ //fb-inner-reg-idxs = <2>; ++ /* 1: rst regs (default in codes), 0: rst the pmic */ ++ pmic-reset-func = <0>; ++ /* not save the PMIC_POWER_EN register in uboot */ ++ not-save-power-en = <1>; ++ ++ vcc1-supply = <&vcc3v3_sys>; ++ vcc2-supply = <&vcc3v3_sys>; ++ vcc3-supply = <&vcc3v3_sys>; ++ vcc4-supply = <&vcc3v3_sys>; ++ vcc5-supply = <&vcc3v3_sys>; ++ vcc6-supply = <&vcc3v3_sys>; ++ vcc7-supply = <&vcc3v3_sys>; ++ vcc8-supply = <&vcc3v3_sys>; ++ vcc9-supply = <&vcc3v3_sys>; ++ wakeup-source; ++ ++ pwrkey { ++ status = "okay"; ++ }; ++ ++ pinctrl_rk8xx: pinctrl_rk8xx { ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ rk817_slppin_null: rk817_slppin_null { ++ pins = "gpio_slp"; ++ function = "pin_fun0"; ++ }; ++ ++ rk817_slppin_slp: rk817_slppin_slp { ++ pins = "gpio_slp"; ++ function = "pin_fun1"; ++ }; ++ ++ rk817_slppin_pwrdn: rk817_slppin_pwrdn { ++ pins = "gpio_slp"; ++ function = "pin_fun2"; ++ }; ++ ++ rk817_slppin_rst: rk817_slppin_rst { ++ pins = "gpio_slp"; ++ function = "pin_fun3"; ++ }; ++ }; ++ ++ regulators { ++ vdd_logic: DCDC_REG1 { ++ regulator-name = "vdd_logic"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-init-microvolt = <900000>; ++ regulator-initial-mode = <0x2>; ++ regulator-min-microvolt = <500000>; ++ regulator-max-microvolt = <1350000>; ++ regulator-ramp-delay = <6001>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vdd_gpu: DCDC_REG2 { ++ regulator-name = "vdd_gpu"; ++ regulator-init-microvolt = <900000>; ++ regulator-initial-mode = <0x2>; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <500000>; ++ regulator-max-microvolt = <1350000>; ++ regulator-ramp-delay = <6001>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcc_ddr: DCDC_REG3 { ++ regulator-name = "vcc_ddr"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-initial-mode = <0x2>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ }; ++ }; ++ ++ vdd_npu: DCDC_REG4 { ++ regulator-name = "vdd_npu"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-init-microvolt = <900000>; ++ regulator-initial-mode = <0x2>; ++ regulator-min-microvolt = <500000>; ++ regulator-max-microvolt = <1350000>; ++ regulator-ramp-delay = <6001>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcc_1v8: DCDC_REG5 { ++ regulator-name = "vcc_1v8"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vdda0v9_image: LDO_REG1 { ++ regulator-name = "vdda0v9_image"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vdda_0v9: LDO_REG2 { ++ regulator-name = "vdda_0v9"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vdda0v9_pmu: LDO_REG3 { ++ regulator-name = "vdda0v9_pmu"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <900000>; ++ }; ++ }; ++ ++ vccio_acodec: LDO_REG4 { ++ regulator-name = "vccio_acodec"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vccio_sd: LDO_REG5 { ++ regulator-name = "vccio_sd"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcc3v3_pmu: LDO_REG6 { ++ regulator-name = "vcc3v3_pmu"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <3300000>; ++ }; ++ }; ++ ++ vcca_1v8: LDO_REG7 { ++ regulator-name = "vcca_1v8"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcca1v8_pmu: LDO_REG8 { ++ regulator-name = "vcca1v8_pmu"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1800000>; ++ }; ++ }; ++ ++ vcca1v8_image: LDO_REG9 { ++ regulator-name = "vcca1v8_image"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcc_3v3: SWITCH_REG1 { ++ regulator-name = "vcc_3v3"; ++ regulator-always-on; ++ regulator-boot-on; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcc3v3_sd: SWITCH_REG2 { ++ regulator-name = "vcc3v3_sd"; ++ regulator-always-on; ++ regulator-boot-on; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ }; ++ ++ rk809_codec: codec { ++ #sound-dai-cells = <0>; ++ compatible = "rockchip,rk809-codec", "rockchip,rk817-codec"; ++ clocks = <&cru I2S1_MCLKOUT_TX>; ++ clock-names = "mclk"; ++ assigned-clocks = <&cru I2S1_MCLKOUT_TX>; ++ assigned-clock-parents = <&cru CLK_I2S1_8CH_TX>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s1m0_mclk>; ++ hp-volume = <20>; ++ spk-volume = <3>; ++ mic-in-differential; ++ status = "okay"; ++ }; ++ }; ++}; ++ ++ ++&mdio1 { ++ rgmii_phy1: phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0x0>; ++ }; ++}; ++ ++&pinctrl { ++ leds { ++ user_led_enable_h: user-led-enable-h { ++ rockchip,pins = <0 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ wifi_led: wifi-led { ++ rockchip,pins = <2 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ sdio-pwrseq { ++ wifi_enable_h: wifi-enable-h { ++ rockchip,pins = <3 RK_PD4 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ usb { ++ vcc5v0_host_en: vcc5v0-host-en { ++ rockchip,pins = <0 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ vcc5v0_otg_en: vcc5v0-otg-en { ++ rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ vcc5v0_hub_en: vcc5v0-hub-en { ++ rockchip,pins = <0 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ pmic { ++ pmic_int: pmic_int { ++ rockchip,pins = ++ <0 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ ++ soc_slppin_gpio: soc_slppin_gpio { ++ rockchip,pins = ++ <0 RK_PA2 RK_FUNC_GPIO &pcfg_output_low>; ++ }; ++ ++ soc_slppin_slp: soc_slppin_slp { ++ rockchip,pins = ++ <0 RK_PA2 1 &pcfg_pull_none>; ++ }; ++ ++ soc_slppin_rst: soc_slppin_rst { ++ rockchip,pins = ++ <0 RK_PA2 2 &pcfg_pull_none>; ++ }; ++ }; ++ ++ wireless-wlan { ++ wifi_host_wake_irq: wifi-host-wake-irq { ++ rockchip,pins = <3 RK_PD5 RK_FUNC_GPIO &pcfg_pull_down>; ++ }; ++ }; ++ ++ wireless-bluetooth { ++ uart1_gpios: uart1-gpios { ++ rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ headphone { ++ hp_det: hp-det { ++ rockchip,pins = <2 RK_PD2 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; ++}; ++ ++&i2s0_8ch { ++ status = "okay"; ++}; ++ ++&pmu_io_domains { ++ pmuio1-supply = <&vcc3v3_pmu>; ++ pmuio2-supply = <&vcc3v3_pmu>; ++ vccio1-supply = <&vccio_acodec>; ++ vccio3-supply = <&vccio_sd>; ++ vccio4-supply = <&vcc_1v8>; ++ vccio5-supply = <&vcc_3v3>; ++ vccio6-supply = <&vcc_1v8>; ++ vccio7-supply = <&vcc_3v3>; ++ status = "okay"; ++}; ++ ++&saradc { ++ vref-supply = <&vcca_1v8>; ++ status = "okay"; ++}; ++ ++&sdhci { ++ bus-width = <8>; ++ max-frequency = <200000000>; ++ non-removable; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&emmc_bus8 &emmc_clk &emmc_cmd &emmc_datastrobe>; ++ status = "okay"; ++}; ++ ++&sdmmc0 { ++ bus-width = <4>; ++ cap-sd-highspeed; ++ cd-gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_LOW>; ++ disable-wp; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdmmc0_bus4 &sdmmc0_clk &sdmmc0_cmd &sdmmc0_det>; ++ sd-uhs-sdr104; ++ vmmc-supply = <&vcc3v3_sd>; ++ vqmmc-supply = <&vccio_sd>; ++ status = "okay"; ++}; ++ ++&tsadc { ++ status = "okay"; ++}; ++ ++&uart2 { ++ status = "okay"; ++}; ++ ++&vop { ++ status = "okay"; ++ assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>; ++ assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>; ++}; ++ ++&vop_mmu { ++ status = "okay"; ++}; ++ ++&vp0 { ++ vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI { ++ reg = ; ++ remote-endpoint = <&hdmi_in_vp0>; ++ }; ++}; ++ ++&hdmi_sound { ++ status = "okay"; ++}; ++ ++&gpu { ++ mali-supply = <&vdd_gpu>; ++ status = "okay"; ++}; ++ ++&u2phy0_host { ++ phy-supply = <&vcc5v0_host>; ++ status = "okay"; ++}; ++ ++&u2phy0_otg { ++ vbus-supply = <&vcc5v0_otg>; ++ status = "okay"; ++}; ++ ++&u2phy1_host { ++ phy-supply = <&vcc5v0_host>; ++ status = "okay"; ++}; ++ ++&u2phy1_otg { ++ phy-supply = <&vcc5v0_host>; ++ status = "okay"; ++}; ++ ++&u2phy0 { ++ status = "okay"; ++}; ++ ++&u2phy1 { ++ status = "okay"; ++}; ++ ++&usbdrd_dwc3 { ++ status = "okay"; ++ extcon = <&u2phy0>; ++}; ++ ++&usbdrd30 { ++ status = "okay"; ++}; ++ ++&usbhost_dwc3 { ++ status = "okay"; ++}; ++ ++&usbhost30 { ++ status = "okay"; ++}; ++ ++&usb_host0_ehci { ++ status = "okay"; ++}; ++ ++&usb_host0_ohci { ++ status = "okay"; ++}; ++ ++&usb_host1_ehci { ++ status = "okay"; ++}; ++ ++&usb_host1_ohci { ++ status = "okay"; ++}; ++ ++&pcie30phy { ++ status = "okay"; ++}; ++ ++&pcie3x2 { ++ reset-gpios = <&gpio2 RK_PD6 GPIO_ACTIVE_HIGH>; ++ vpcie3v3-supply = <&pcie30_3v3>; ++ //num-lanes = <2>; ++ pinctrl-0 = <&pcie30x2m1_pins>; ++ bus-scan-delay-ms = <1000>; ++ status = "okay"; ++}; ++ ++&pcie2x1 { ++ reset-gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_HIGH>; ++ vpcie3v3-supply = <&pcie30_3v3>; ++ pinctrl-0 = <&pcie20m1_pins>; ++ bus-scan-delay-ms = <1000>; ++ status = "okay"; ++}; diff --git a/patch/kernel/archive/rk35xx-5.17/rk3568-pwm-rockchip.patch b/patch/kernel/archive/rk35xx-5.17/rk3568-pwm-rockchip.patch new file mode 100644 index 0000000000..9a4f15d238 --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk3568-pwm-rockchip.patch @@ -0,0 +1,428 @@ +diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c +index f3647b317152..7905deaf7055 100644 +--- a/drivers/pwm/pwm-rockchip.c ++++ b/drivers/pwm/pwm-rockchip.c +@@ -1,9 +1,12 @@ +-// SPDX-License-Identifier: GPL-2.0-only + /* + * PWM driver for Rockchip SoCs + * + * Copyright (C) 2014 Beniamino Galvani + * Copyright (C) 2014 ROCKCHIP, Inc. ++ * ++ * 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 +@@ -11,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -26,15 +30,25 @@ + #define PWM_INACTIVE_POSITIVE (1 << 4) + #define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE) + #define PWM_OUTPUT_LEFT (0 << 5) ++#define PWM_OUTPUT_CENTER (1 << 5) + #define PWM_LOCK_EN (1 << 6) + #define PWM_LP_DISABLE (0 << 8) + ++#define PWM_ONESHOT_COUNT_SHIFT 24 ++#define PWM_ONESHOT_COUNT_MAX 256 ++ + struct rockchip_pwm_chip { + struct pwm_chip chip; + struct clk *clk; + struct clk *pclk; ++ struct pinctrl *pinctrl; ++ struct pinctrl_state *active_state; + const struct rockchip_pwm_data *data; + void __iomem *base; ++ unsigned long clk_rate; ++ bool vop_pwm_en; /* indicate voppwm mirror register state */ ++ bool center_aligned; ++ bool oneshot; + }; + + struct rockchip_pwm_regs { +@@ -49,7 +63,9 @@ struct rockchip_pwm_data { + unsigned int prescaler; + bool supports_polarity; + bool supports_lock; ++ bool vop_pwm; + u32 enable_conf; ++ u32 enable_conf_mask; + }; + + static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) +@@ -63,7 +79,6 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, + { + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u32 enable_conf = pc->data->enable_conf; +- unsigned long clk_rate; + u64 tmp; + u32 val; + int ret; +@@ -72,59 +87,77 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, + if (ret) + return; + +- ret = clk_enable(pc->clk); +- if (ret) +- return; +- +- clk_rate = clk_get_rate(pc->clk); +- + tmp = readl_relaxed(pc->base + pc->data->regs.period); + tmp *= pc->data->prescaler * NSEC_PER_SEC; +- state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); ++ state->period = DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate); + + tmp = readl_relaxed(pc->base + pc->data->regs.duty); + tmp *= pc->data->prescaler * NSEC_PER_SEC; +- state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); ++ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate); + + val = readl_relaxed(pc->base + pc->data->regs.ctrl); +- state->enabled = (val & enable_conf) == enable_conf; +- +- if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE)) +- state->polarity = PWM_POLARITY_INVERSED; ++ if (pc->data->supports_polarity) ++ state->enabled = ((val & enable_conf) != enable_conf) ? ++ false : true; + else +- state->polarity = PWM_POLARITY_NORMAL; ++ state->enabled = ((val & enable_conf) == enable_conf) ? ++ true : false; ++ ++ if (pc->data->supports_polarity) { ++ if (!(val & PWM_DUTY_POSITIVE)) ++ state->polarity = PWM_POLARITY_INVERSED; ++ } + +- clk_disable(pc->clk); + clk_disable(pc->pclk); + } + + static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, +- const struct pwm_state *state) ++ struct pwm_state *state) + { + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + unsigned long period, duty; +- u64 clk_rate, div; ++ unsigned long flags; ++ u64 div; + u32 ctrl; + +- clk_rate = clk_get_rate(pc->clk); +- + /* + * Since period and duty cycle registers have a width of 32 + * bits, every possible input period can be obtained using the + * default prescaler value for all practical clock rate values. + */ +- div = clk_rate * state->period; ++ div = (u64)pc->clk_rate * state->period; + period = DIV_ROUND_CLOSEST_ULL(div, + pc->data->prescaler * NSEC_PER_SEC); + +- div = clk_rate * state->duty_cycle; ++ div = (u64)pc->clk_rate * state->duty_cycle; + duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); + ++ local_irq_save(flags); + /* + * Lock the period and duty of previous configuration, then + * change the duty and period, that would not be effective. + */ + ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); ++ if (pc->data->vop_pwm) { ++ if (pc->vop_pwm_en) ++ ctrl |= PWM_ENABLE; ++ else ++ ctrl &= ~PWM_ENABLE; ++ } ++ ++#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT ++ if (state->oneshot_count > PWM_ONESHOT_COUNT_MAX) { ++ pc->oneshot = false; ++ dev_err(chip->dev, "Oneshot_count value overflow.\n"); ++ } else if (state->oneshot_count > 0) { ++ pc->oneshot = true; ++ ctrl |= (state->oneshot_count - 1) << PWM_ONESHOT_COUNT_SHIFT; ++ } else { ++ pc->oneshot = false; ++ ctrl |= PWM_CONTINUOUS; ++ } ++#endif ++ + if (pc->data->supports_lock) { + ctrl |= PWM_LOCK_EN; + writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl); +@@ -150,6 +183,7 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + ctrl &= ~PWM_LOCK_EN; + + writel(ctrl, pc->base + pc->data->regs.ctrl); ++ local_irq_restore(flags); + } + + static int rockchip_pwm_enable(struct pwm_chip *chip, +@@ -168,13 +202,24 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, + } + + val = readl_relaxed(pc->base + pc->data->regs.ctrl); ++ val &= ~pc->data->enable_conf_mask; ++ ++ if (PWM_OUTPUT_CENTER & pc->data->enable_conf_mask) { ++ if (pc->center_aligned) ++ val |= PWM_OUTPUT_CENTER; ++ } + +- if (enable) ++ if (enable) { + val |= enable_conf; +- else ++ if (pc->oneshot) ++ val &= ~PWM_CONTINUOUS; ++ } else { + val &= ~enable_conf; ++ } + + writel_relaxed(val, pc->base + pc->data->regs.ctrl); ++ if (pc->data->vop_pwm) ++ pc->vop_pwm_en = enable; + + if (!enable) + clk_disable(pc->clk); +@@ -194,10 +239,6 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + if (ret) + return ret; + +- ret = clk_enable(pc->clk); +- if (ret) +- return ret; +- + pwm_get_state(pwm, &curstate); + enabled = curstate.enabled; + +@@ -216,8 +257,15 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + goto out; + } + ++ /* ++ * Update the state with the real hardware, which can differ a bit ++ * because of period/duty_cycle approximation. ++ */ ++ rockchip_pwm_get_state(chip, pwm, state); ++ ++ if (state->enabled || pc->oneshot) ++ ret = pinctrl_select_state(pc->pinctrl, pc->active_state); + out: +- clk_disable(pc->clk); + clk_disable(pc->pclk); + + return ret; +@@ -239,7 +287,9 @@ static const struct rockchip_pwm_data pwm_data_v1 = { + .prescaler = 2, + .supports_polarity = false, + .supports_lock = false, ++ .vop_pwm = false, + .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN, ++ .enable_conf_mask = BIT(1) | BIT(3), + }; + + static const struct rockchip_pwm_data pwm_data_v2 = { +@@ -252,8 +302,10 @@ static const struct rockchip_pwm_data pwm_data_v2 = { + .prescaler = 1, + .supports_polarity = true, + .supports_lock = false, ++ .vop_pwm = false, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, ++ .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8), + }; + + static const struct rockchip_pwm_data pwm_data_vop = { +@@ -266,8 +318,10 @@ static const struct rockchip_pwm_data pwm_data_vop = { + .prescaler = 1, + .supports_polarity = true, + .supports_lock = false, ++ .vop_pwm = true, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, ++ .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8), + }; + + static const struct rockchip_pwm_data pwm_data_v3 = { +@@ -280,8 +334,10 @@ static const struct rockchip_pwm_data pwm_data_v3 = { + .prescaler = 1, + .supports_polarity = true, + .supports_lock = true, ++ .vop_pwm = false, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, ++ .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8), + }; + + static const struct of_device_id rockchip_pwm_dt_ids[] = { +@@ -297,8 +353,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + { + const struct of_device_id *id; + struct rockchip_pwm_chip *pc; +- u32 enable_conf, ctrl; +- bool enabled; ++ struct resource *r; + int ret, count; + + id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); +@@ -309,16 +364,22 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + if (!pc) + return -ENOMEM; + +- pc->base = devm_platform_ioremap_resource(pdev, 0); ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ pc->base = devm_ioremap(&pdev->dev, r->start, ++ resource_size(r)); + if (IS_ERR(pc->base)) + return PTR_ERR(pc->base); + + pc->clk = devm_clk_get(&pdev->dev, "pwm"); + if (IS_ERR(pc->clk)) { + pc->clk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(pc->clk)) +- return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk), +- "Can't get PWM clk\n"); ++ if (IS_ERR(pc->clk)) { ++ ret = PTR_ERR(pc->clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "Can't get bus clk: %d\n", ++ ret); ++ return ret; ++ } + } + + count = of_count_phandle_with_args(pdev->dev.of_node, +@@ -337,26 +398,44 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + + ret = clk_prepare_enable(pc->clk); + if (ret) { +- dev_err(&pdev->dev, "Can't prepare enable PWM clk: %d\n", ret); ++ dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret); + return ret; + } + +- ret = clk_prepare_enable(pc->pclk); ++ ret = clk_prepare(pc->pclk); + if (ret) { +- dev_err(&pdev->dev, "Can't prepare enable APB clk: %d\n", ret); ++ dev_err(&pdev->dev, "Can't prepare APB clk: %d\n", ret); + goto err_clk; + } + ++ pc->pinctrl = devm_pinctrl_get(&pdev->dev); ++ if (IS_ERR(pc->pinctrl)) { ++ dev_err(&pdev->dev, "Get pinctrl failed!\n"); ++ return PTR_ERR(pc->pinctrl); ++ } ++ ++ pc->active_state = pinctrl_lookup_state(pc->pinctrl, "active"); ++ if (IS_ERR(pc->active_state)) { ++ dev_err(&pdev->dev, "No active pinctrl state\n"); ++ return PTR_ERR(pc->active_state); ++ } ++ + platform_set_drvdata(pdev, pc); + + pc->data = id->data; + pc->chip.dev = &pdev->dev; + pc->chip.ops = &rockchip_pwm_ops; ++ pc->chip.base = -1; + pc->chip.npwm = 1; ++ pc->clk_rate = clk_get_rate(pc->clk); + +- enable_conf = pc->data->enable_conf; +- ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); +- enabled = (ctrl & enable_conf) == enable_conf; ++ if (pc->data->supports_polarity) { ++ pc->chip.of_xlate = of_pwm_xlate_with_flags; ++ pc->chip.of_pwm_n_cells = 3; ++ } ++ ++ pc->center_aligned = ++ device_property_read_bool(&pdev->dev, "center-aligned"); + + ret = pwmchip_add(&pc->chip); + if (ret < 0) { +@@ -365,15 +444,13 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + } + + /* Keep the PWM clk enabled if the PWM appears to be up and running. */ +- if (!enabled) ++ if (!pwm_is_enabled(pc->chip.pwms)) + clk_disable(pc->clk); + +- clk_disable(pc->pclk); +- + return 0; + + err_pclk: +- clk_disable_unprepare(pc->pclk); ++ clk_unprepare(pc->pclk); + err_clk: + clk_disable_unprepare(pc->clk); + +@@ -384,11 +461,24 @@ static int rockchip_pwm_remove(struct platform_device *pdev) + { + struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev); + +- pwmchip_remove(&pc->chip); ++ /* ++ * Disable the PWM clk before unpreparing it if the PWM device is still ++ * running. This should only happen when the last PWM user left it ++ * enabled, or when nobody requested a PWM that was previously enabled ++ * by the bootloader. ++ * ++ * FIXME: Maybe the core should disable all PWM devices in ++ * pwmchip_remove(). In this case we'd only have to call ++ * clk_unprepare() after pwmchip_remove(). ++ * ++ */ ++ if (pwm_is_enabled(pc->chip.pwms)) ++ clk_disable(pc->clk); + + clk_unprepare(pc->pclk); + clk_unprepare(pc->clk); + ++ pwmchip_remove(&pc->chip); + return 0; + } + +@@ -400,7 +490,21 @@ static struct platform_driver rockchip_pwm_driver = { + .probe = rockchip_pwm_probe, + .remove = rockchip_pwm_remove, + }; ++#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT ++static int __init rockchip_pwm_driver_init(void) ++{ ++ return platform_driver_register(&rockchip_pwm_driver); ++} ++subsys_initcall(rockchip_pwm_driver_init); ++ ++static void __exit rockchip_pwm_driver_exit(void) ++{ ++ platform_driver_unregister(&rockchip_pwm_driver); ++} ++module_exit(rockchip_pwm_driver_exit); ++#else + module_platform_driver(rockchip_pwm_driver); ++#endif + + MODULE_AUTHOR("Beniamino Galvani "); + MODULE_DESCRIPTION("Rockchip SoC PWM driver"); diff --git a/patch/kernel/archive/rk35xx-5.17/rk356x-add-dtb-overlay-support.patch b/patch/kernel/archive/rk35xx-5.17/rk356x-add-dtb-overlay-support.patch new file mode 100644 index 0000000000..c2fe8e29ea --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk356x-add-dtb-overlay-support.patch @@ -0,0 +1,12 @@ +diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib +index d1f865b8c0cb..998845187686 100644 +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -295,6 +295,7 @@ quiet_cmd_gzip = GZIP $@ + # --------------------------------------------------------------------------- + DTC ?= $(objtree)/scripts/dtc/dtc + DTC_FLAGS += -Wno-interrupt_provider ++DTC_FLAGS += -@ + + # Disable noisy checks by default + ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),) diff --git a/patch/kernel/archive/rk35xx-5.17/rk356x-drm-rockchip-VOP2-support.patch b/patch/kernel/archive/rk35xx-5.17/rk356x-drm-rockchip-VOP2-support.patch new file mode 100644 index 0000000000..f4190fcd2e --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk356x-drm-rockchip-VOP2-support.patch @@ -0,0 +1,5157 @@ +diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml +index da3b889ad8fc..fc26f1d4d001 100644 +--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml ++++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml +@@ -23,20 +23,34 @@ properties: + - rockchip,rk3288-dw-hdmi + - rockchip,rk3328-dw-hdmi + - rockchip,rk3399-dw-hdmi ++ - rockchip,rk3568-dw-hdmi + + reg-io-width: + const: 4 + ++ avdd-0v9-supply: ++ description: ++ A 0.9V supply that powers up the SoC internal circuitry. The actual pin name ++ varies between the different SoCs and is usually HDMI_TX_AVDD_0V9 or sometimes ++ HDMI_AVDD_1V0. ++ ++ avdd-1v8-supply: ++ description: ++ A 1.8V supply that powers up the SoC internal circuitry. The pin name on the ++ SoC usually is HDMI_TX_AVDD_1V8. ++ + clocks: + minItems: 2 + items: + - {} + - {} +- # The next three clocks are all optional, but shall be specified in this ++ # The next four clocks are all optional, but shall be specified in this + # order when present. + - description: The HDMI CEC controller main clock + - description: Power for GRF IO +- - description: External clock for some HDMI PHY ++ - description: External clock for some HDMI PHY (old clock name, deprecated) ++ - description: External clock for some HDMI PHY (new name) ++ - description: hclk + + clock-names: + minItems: 2 +@@ -47,10 +61,18 @@ properties: + - cec + - grf + - vpll ++ - ref ++ - hclk + - enum: + - grf + - vpll +- - const: vpll ++ - ref ++ - hclk ++ - enum: ++ - vpll ++ - ref ++ - hclk ++ - const: hclk + + ddc-i2c-bus: + $ref: /schemas/types.yaml#/definitions/phandle +@@ -72,6 +94,7 @@ properties: + The unwedge pinctrl entry shall drive the DDC SDA line low. This is + intended to work around a hardware errata that can cause the DDC I2C + bus to be wedged. ++ minItems: 1 + items: + - const: default + - const: unwedge +@@ -79,27 +102,21 @@ properties: + ports: + $ref: /schemas/graph.yaml#/properties/ports + +- properties: +- port: +- $ref: /schemas/graph.yaml#/$defs/port-base +- unevaluatedProperties: false ++ patternProperties: ++ "^port(@0)?$": ++ $ref: /schemas/graph.yaml#/properties/port + description: Input of the DWC HDMI TX +- + properties: ++ endpoint: ++ description: Connection to the VOP + endpoint@0: +- $ref: /schemas/graph.yaml#/properties/endpoint + description: Connection to the VOPB +- + endpoint@1: +- $ref: /schemas/graph.yaml#/properties/endpoint + description: Connection to the VOPL +- +- required: +- - endpoint@0 +- - endpoint@1 +- +- required: +- - port ++ properties: ++ port@1: ++ $ref: /schemas/graph.yaml#/properties/port ++ description: Output of the DWC HDMI TX + + rockchip,grf: + $ref: /schemas/types.yaml#/definitions/phandle +diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml +new file mode 100644 +index 000000000000..655d9b327f7d +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml +@@ -0,0 +1,140 @@ ++# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/display/rockchip/rockchip-vop2.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Rockchip SoC display controller (VOP2) ++ ++description: ++ VOP2 (Video Output Processor v2) is the display controller for the Rockchip ++ series of SoCs which transfers the image data from a video memory ++ buffer to an external LCD interface. ++ ++maintainers: ++ - Sandy Huang ++ - Heiko Stuebner ++ ++properties: ++ compatible: ++ enum: ++ - rockchip,rk3566-vop ++ - rockchip,rk3568-vop ++ ++ reg: ++ minItems: 1 ++ items: ++ - description: ++ Must contain one entry corresponding to the base address and length ++ of the register space. ++ - description: ++ Can optionally contain a second entry corresponding to ++ the CRTC gamma LUT address. ++ ++ interrupts: ++ maxItems: 1 ++ description: ++ The VOP interrupt is shared by several interrupt sources, such as ++ frame start (VSYNC), line flag and other status interrupts. ++ ++ clocks: ++ items: ++ - description: Clock for ddr buffer transfer. ++ - description: Clock for the ahb bus to R/W the phy regs. ++ - description: Pixel clock for video port 0. ++ - description: Pixel clock for video port 1. ++ - description: Pixel clock for video port 2. ++ ++ clock-names: ++ items: ++ - const: aclk ++ - const: hclk ++ - const: dclk_vp0 ++ - const: dclk_vp1 ++ - const: dclk_vp2 ++ ++ rockchip,grf: ++ $ref: /schemas/types.yaml#/definitions/phandle ++ description: ++ Phandle to GRF regs used for misc control ++ ++ ports: ++ $ref: /schemas/graph.yaml#/properties/ports ++ ++ properties: ++ port@0: ++ $ref: /schemas/graph.yaml#/properties/port ++ description: ++ Output endpoint of VP0 ++ ++ port@1: ++ $ref: /schemas/graph.yaml#/properties/port ++ description: ++ Output endpoint of VP1 ++ ++ port@2: ++ $ref: /schemas/graph.yaml#/properties/port ++ description: ++ Output endpoint of VP2 ++ ++ iommus: ++ maxItems: 1 ++ ++ power-domains: ++ maxItems: 1 ++ ++required: ++ - compatible ++ - reg ++ - interrupts ++ - clocks ++ - clock-names ++ - ports ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ #include ++ #include ++ bus { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ vop: vop@fe040000 { ++ compatible = "rockchip,rk3568-vop"; ++ reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>; ++ reg-names = "regs", "gamma_lut"; ++ interrupts = ; ++ clocks = <&cru ACLK_VOP>, ++ <&cru HCLK_VOP>, ++ <&cru DCLK_VOP0>, ++ <&cru DCLK_VOP1>, ++ <&cru DCLK_VOP2>; ++ clock-names = "aclk", ++ "hclk", ++ "dclk_vp0", ++ "dclk_vp1", ++ "dclk_vp2"; ++ power-domains = <&power RK3568_PD_VO>; ++ iommus = <&vop_mmu>; ++ vop_out: ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ vp0: port@0 { ++ reg = <0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ vp1: port@1 { ++ reg = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ vp2: port@2 { ++ reg = <2>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ }; ++ }; +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +index 4d4b2a301b1a..d8640a9be12f 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +@@ -4,6 +4,7 @@ + + #include + #include ++#include + #include "rk3566.dtsi" + + / { +@@ -35,6 +36,17 @@ fan: gpio_fan { + #cooling-cells = <2>; + }; + ++ hdmi-con { ++ compatible = "hdmi-connector"; ++ type = "a"; ++ ++ port { ++ hdmi_con_in: endpoint { ++ remote-endpoint = <&hdmi_out_con>; ++ }; ++ }; ++ }; ++ + leds { + compatible = "gpio-leds"; + +@@ -205,6 +217,24 @@ &gmac1m0_clkinout + status = "okay"; + }; + ++&hdmi { ++ avdd-0v9-supply = <&vdda_0v9>; ++ avdd-1v8-supply = <&vcc_1v8>; ++ status = "okay"; ++}; ++ ++&hdmi_in { ++ hdmi_in_vp0: endpoint { ++ remote-endpoint = <&vp0_out_hdmi>; ++ }; ++}; ++ ++&hdmi_out { ++ hdmi_out_con: endpoint { ++ remote-endpoint = <&hdmi_con_in>; ++ }; ++}; ++ + &i2c0 { + status = "okay"; + +@@ -546,3 +576,20 @@ bluetooth { + &uart2 { + status = "okay"; + }; ++ ++&vop { ++ assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>; ++ assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>; ++ status = "okay"; ++}; ++ ++&vop_mmu { ++ status = "okay"; ++}; ++ ++&vp0 { ++ vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI { ++ reg = ; ++ remote-endpoint = <&hdmi_in_vp0>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/rockchip/rk3566.dtsi b/arch/arm64/boot/dts/rockchip/rk3566.dtsi +index 3839eef5e4f7..595fa2562cb8 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3566.dtsi +@@ -18,3 +18,7 @@ power-domain@RK3568_PD_PIPE { + #power-domain-cells = <0>; + }; + }; ++ ++&vop { ++ compatible = "rockchip,rk3566-vop"; ++}; +diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts +index 184e2aa2416a..cd4e01c7994c 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts +@@ -7,6 +7,7 @@ + /dts-v1/; + #include + #include ++#include + #include "rk3568.dtsi" + + / { +@@ -33,6 +34,17 @@ dc_12v: dc-12v { + regulator-max-microvolt = <12000000>; + }; + ++ hdmi-con { ++ compatible = "hdmi-connector"; ++ type = "a"; ++ ++ port { ++ hdmi_con_in: endpoint { ++ remote-endpoint = <&hdmi_out_con>; ++ }; ++ }; ++ }; ++ + vcc3v3_sys: vcc3v3-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_sys"; +@@ -106,6 +118,24 @@ &gmac1m1_rgmii_clk + status = "okay"; + }; + ++&hdmi { ++ avdd-0v9-supply = <&vdda0v9_image>; ++ avdd-1v8-supply = <&vcca1v8_image>; ++ status = "okay"; ++}; ++ ++&hdmi_in { ++ hdmi_in_vp0: endpoint { ++ remote-endpoint = <&vp0_out_hdmi>; ++ }; ++}; ++ ++&hdmi_out { ++ hdmi_out_con: endpoint { ++ remote-endpoint = <&hdmi_con_in>; ++ }; ++}; ++ + &i2c0 { + status = "okay"; + +@@ -390,3 +420,20 @@ &sdmmc0 { + &uart2 { + status = "okay"; + }; ++ ++&vop { ++ assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>; ++ assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>; ++ status = "okay"; ++}; ++ ++&vop_mmu { ++ status = "okay"; ++}; ++ ++&vp0 { ++ vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI { ++ reg = ; ++ remote-endpoint = <&hdmi_in_vp0>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi +index 2fd313a295f8..1e55efb6fcfd 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi +@@ -95,3 +95,7 @@ power-domain@RK3568_PD_PIPE { + #power-domain-cells = <0>; + }; + }; ++ ++&vop { ++ compatible = "rockchip,rk3568-vop"; ++}; +diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +index 46d9552f6028..cf62bce06695 100644 +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -125,6 +125,11 @@ opp-1800000000 { + }; + }; + ++ display_subsystem: display-subsystem { ++ compatible = "rockchip,display-subsystem"; ++ ports = <&vop_out>; ++ }; ++ + firmware { + scmi: scmi { + compatible = "arm,scmi-smc"; +@@ -447,6 +452,84 @@ gmac1_mtl_tx_setup: tx-queues-config { + }; + }; + ++ vop: vop@fe040000 { ++ reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>; ++ reg-names = "regs", "gamma_lut"; ++ interrupts = ; ++ clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>, <&cru DCLK_VOP0>, ++ <&cru DCLK_VOP1>, <&cru DCLK_VOP2>; ++ clock-names = "aclk", "hclk", "dclk_vp0", "dclk_vp1", "dclk_vp2"; ++ iommus = <&vop_mmu>; ++ power-domains = <&power RK3568_PD_VO>; ++ rockchip,grf = <&grf>; ++ status = "disabled"; ++ ++ vop_out: ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ vp0: port@0 { ++ reg = <0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ vp1: port@1 { ++ reg = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ vp2: port@2 { ++ reg = <2>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ }; ++ ++ vop_mmu: iommu@fe043e00 { ++ compatible = "rockchip,rk3568-iommu"; ++ reg = <0x0 0xfe043e00 0x0 0x100>, <0x0 0xfe043f00 0x0 0x100>; ++ interrupts = ; ++ clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>; ++ clock-names = "aclk", "iface"; ++ #iommu-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ hdmi: hdmi@fe0a0000 { ++ compatible = "rockchip,rk3568-dw-hdmi"; ++ reg = <0x0 0xfe0a0000 0x0 0x20000>; ++ interrupts = ; ++ clocks = <&cru PCLK_HDMI_HOST>, ++ <&cru CLK_HDMI_SFR>, ++ <&cru CLK_HDMI_CEC>, ++ <&pmucru CLK_HDMI_REF>, ++ <&cru HCLK_VOP>; ++ clock-names = "iahb", "isfr", "cec", "ref", "hclk"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hdmitx_scl &hdmitx_sda &hdmitxm0_cec>; ++ power-domains = <&power RK3568_PD_VO>; ++ reg-io-width = <4>; ++ rockchip,grf = <&grf>; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hdmi_in: port@0 { ++ reg = <0>; ++ }; ++ ++ hdmi_out: port@1 { ++ reg = <1>; ++ }; ++ }; ++ }; ++ + qos_gpu: qos@fe128000 { + compatible = "rockchip,rk3568-qos", "syscon"; + reg = <0x0 0xfe128000 0x0 0x20>; +diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig +index 9f1ecefc3933..4ff0043f0ee7 100644 +--- a/drivers/gpu/drm/rockchip/Kconfig ++++ b/drivers/gpu/drm/rockchip/Kconfig +@@ -21,8 +21,22 @@ config DRM_ROCKCHIP + + if DRM_ROCKCHIP + ++config ROCKCHIP_VOP ++ bool "Rockchip VOP driver" ++ default y ++ help ++ This selects support for the VOP driver. You should enable it ++ on all older SoCs up to RK3399. ++ ++config ROCKCHIP_VOP2 ++ bool "Rockchip VOP2 driver" ++ help ++ This selects support for the VOP2 driver. You should enable it ++ on all newer SoCs beginning form RK3568. ++ + config ROCKCHIP_ANALOGIX_DP + bool "Rockchip specific extensions for Analogix DP driver" ++ depends on ROCKCHIP_VOP + help + This selects support for Rockchip SoC specific extensions + for the Analogix Core DP driver. If you want to enable DP +diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile +index 1a56f696558c..153bca4090e2 100644 +--- a/drivers/gpu/drm/rockchip/Makefile ++++ b/drivers/gpu/drm/rockchip/Makefile +@@ -4,7 +4,9 @@ + # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + + rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ +- rockchip_drm_gem.o rockchip_drm_vop.o rockchip_vop_reg.o ++ rockchip_drm_gem.o ++rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o ++rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o + + rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o + rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o +diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +index 8abb5ac26807..bb33c6c217f7 100644 +--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c ++++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +@@ -40,8 +40,6 @@ + + #define PSR_WAIT_LINE_FLAG_TIMEOUT_MS 100 + +-#define to_dp(nm) container_of(nm, struct rockchip_dp_device, nm) +- + /** + * struct rockchip_dp_chip_data - splite the grf setting of kind of chips + * @lcdsel_grf_reg: grf register offset of lcdc select +@@ -59,7 +57,7 @@ struct rockchip_dp_chip_data { + struct rockchip_dp_device { + struct drm_device *drm_dev; + struct device *dev; +- struct drm_encoder encoder; ++ struct rockchip_encoder encoder; + struct drm_display_mode mode; + + struct clk *pclk; +@@ -73,6 +71,18 @@ struct rockchip_dp_device { + struct analogix_dp_plat_data plat_data; + }; + ++static struct rockchip_dp_device *encoder_to_dp(struct drm_encoder *encoder) ++{ ++ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); ++ ++ return container_of(rkencoder, struct rockchip_dp_device, encoder); ++} ++ ++static struct rockchip_dp_device *pdata_encoder_to_dp(struct analogix_dp_plat_data *plat_data) ++{ ++ return container_of(plat_data, struct rockchip_dp_device, plat_data); ++} ++ + static int rockchip_dp_pre_init(struct rockchip_dp_device *dp) + { + reset_control_assert(dp->rst); +@@ -84,7 +94,7 @@ static int rockchip_dp_pre_init(struct rockchip_dp_device *dp) + + static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data) + { +- struct rockchip_dp_device *dp = to_dp(plat_data); ++ struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data); + int ret; + + ret = clk_prepare_enable(dp->pclk); +@@ -105,7 +115,7 @@ static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data) + + static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data) + { +- struct rockchip_dp_device *dp = to_dp(plat_data); ++ struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data); + + clk_disable_unprepare(dp->pclk); + +@@ -166,7 +176,7 @@ struct drm_crtc *rockchip_dp_drm_get_new_crtc(struct drm_encoder *encoder, + static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) + { +- struct rockchip_dp_device *dp = to_dp(encoder); ++ struct rockchip_dp_device *dp = encoder_to_dp(encoder); + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; + int ret; +@@ -208,7 +218,7 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder, + static void rockchip_dp_drm_encoder_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) + { +- struct rockchip_dp_device *dp = to_dp(encoder); ++ struct rockchip_dp_device *dp = encoder_to_dp(encoder); + struct drm_crtc *crtc; + struct drm_crtc_state *new_crtc_state = NULL; + int ret; +@@ -297,7 +307,7 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp) + + static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp) + { +- struct drm_encoder *encoder = &dp->encoder; ++ struct drm_encoder *encoder = &dp->encoder.encoder; + struct drm_device *drm_dev = dp->drm_dev; + struct device *dev = dp->dev; + int ret; +@@ -333,7 +343,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, + return ret; + } + +- dp->plat_data.encoder = &dp->encoder; ++ dp->plat_data.encoder = &dp->encoder.encoder; + + ret = analogix_dp_bind(dp->adp, drm_dev); + if (ret) +@@ -341,7 +351,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, + + return 0; + err_cleanup_encoder: +- dp->encoder.funcs->destroy(&dp->encoder); ++ dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder); + return ret; + } + +@@ -351,7 +361,7 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master, + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + analogix_dp_unbind(dp->adp); +- dp->encoder.funcs->destroy(&dp->encoder); ++ dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder); + } + + static const struct component_ops rockchip_dp_component_ops = { +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c +index 16497c31d9f9..6ce1c1cdd9d6 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c ++++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c +@@ -26,11 +26,17 @@ + #include "cdn-dp-reg.h" + #include "rockchip_drm_vop.h" + +-#define connector_to_dp(c) \ +- container_of(c, struct cdn_dp_device, connector) ++static inline struct cdn_dp_device *connector_to_dp(struct drm_connector *connector) ++{ ++ return container_of(connector, struct cdn_dp_device, connector); ++} + +-#define encoder_to_dp(c) \ +- container_of(c, struct cdn_dp_device, encoder) ++static inline struct cdn_dp_device *encoder_to_dp(struct drm_encoder *encoder) ++{ ++ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); ++ ++ return container_of(rkencoder, struct cdn_dp_device, encoder); ++} + + #define GRF_SOC_CON9 0x6224 + #define DP_SEL_VOP_LIT BIT(12) +@@ -1022,7 +1028,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) + + INIT_WORK(&dp->event_work, cdn_dp_pd_event_work); + +- encoder = &dp->encoder; ++ encoder = &dp->encoder.encoder; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, + dev->of_node); +@@ -1087,7 +1093,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) + static void cdn_dp_unbind(struct device *dev, struct device *master, void *data) + { + struct cdn_dp_device *dp = dev_get_drvdata(dev); +- struct drm_encoder *encoder = &dp->encoder; ++ struct drm_encoder *encoder = &dp->encoder.encoder; + struct drm_connector *connector = &dp->connector; + + cancel_work_sync(&dp->event_work); +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h +index 81ac9b658a70..29539170d3b1 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h ++++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h +@@ -65,7 +65,7 @@ struct cdn_dp_device { + struct device *dev; + struct drm_device *drm_dev; + struct drm_connector connector; +- struct drm_encoder encoder; ++ struct rockchip_encoder encoder; + struct drm_display_mode mode; + struct platform_device *audio_pdev; + struct work_struct event_work; +diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +index 4ed7a6868197..110e83aad9bb 100644 +--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +@@ -181,8 +181,6 @@ + + #define HIWORD_UPDATE(val, mask) (val | (mask) << 16) + +-#define to_dsi(nm) container_of(nm, struct dw_mipi_dsi_rockchip, nm) +- + enum { + DW_DSI_USAGE_IDLE, + DW_DSI_USAGE_DSI, +@@ -236,7 +234,7 @@ struct rockchip_dw_dsi_chip_data { + + struct dw_mipi_dsi_rockchip { + struct device *dev; +- struct drm_encoder encoder; ++ struct rockchip_encoder encoder; + void __iomem *base; + + struct regmap *grf_regmap; +@@ -271,6 +269,13 @@ struct dw_mipi_dsi_rockchip { + bool dsi_bound; + }; + ++static struct dw_mipi_dsi_rockchip *to_dsi(struct drm_encoder *encoder) ++{ ++ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); ++ ++ return container_of(rkencoder, struct dw_mipi_dsi_rockchip, encoder); ++} ++ + struct dphy_pll_parameter_map { + unsigned int max_mbps; + u8 hsfreqrange; +@@ -770,7 +775,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder) + int ret, mux; + + mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node, +- &dsi->encoder); ++ &dsi->encoder.encoder); + if (mux < 0) + return; + +@@ -801,7 +806,7 @@ dw_mipi_dsi_encoder_helper_funcs = { + static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi, + struct drm_device *drm_dev) + { +- struct drm_encoder *encoder = &dsi->encoder; ++ struct drm_encoder *encoder = &dsi->encoder.encoder; + int ret; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, +@@ -959,7 +964,7 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev, + goto out_pll_clk; + } + +- ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder); ++ ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder.encoder); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret); + goto out_pll_clk; +diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +index 8677c8271678..2c9e3d82fbc6 100644 +--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -50,6 +51,10 @@ + #define RK3399_GRF_SOC_CON20 0x6250 + #define RK3399_HDMI_LCDC_SEL BIT(6) + ++#define RK3568_GRF_VO_CON1 0x0364 ++#define RK3568_HDMI_SDAIN_MSK BIT(15) ++#define RK3568_HDMI_SCLIN_MSK BIT(14) ++ + #define HIWORD_UPDATE(val, mask) (val | (mask) << 16) + + /** +@@ -67,92 +72,108 @@ struct rockchip_hdmi_chip_data { + struct rockchip_hdmi { + struct device *dev; + struct regmap *regmap; +- struct drm_encoder encoder; ++ struct rockchip_encoder encoder; + const struct rockchip_hdmi_chip_data *chip_data; +- struct clk *vpll_clk; ++ struct clk *ref_clk; + struct clk *grf_clk; ++ struct clk *hclk_clk; + struct dw_hdmi *hdmi; ++ struct regulator *avdd_0v9; ++ struct regulator *avdd_1v8; + struct phy *phy; + }; + +-#define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) ++static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder) ++{ ++ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); ++ ++ return container_of(rkencoder, struct rockchip_hdmi, encoder); ++} + + static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = { + { +- 27000000, { +- { 0x00b3, 0x0000}, +- { 0x2153, 0x0000}, +- { 0x40f3, 0x0000} ++ 30666000, { ++ { 0x00b3, 0x0000 }, ++ { 0x2153, 0x0000 }, ++ { 0x40f3, 0x0000 }, ++ }, ++ }, { ++ 36800000, { ++ { 0x00b3, 0x0000 }, ++ { 0x2153, 0x0000 }, ++ { 0x40a2, 0x0001 }, + }, +- }, { +- 36000000, { +- { 0x00b3, 0x0000}, +- { 0x2153, 0x0000}, +- { 0x40f3, 0x0000} ++ }, { ++ 46000000, { ++ { 0x00b3, 0x0000 }, ++ { 0x2142, 0x0001 }, ++ { 0x40a2, 0x0001 }, + }, +- }, { +- 40000000, { +- { 0x00b3, 0x0000}, +- { 0x2153, 0x0000}, +- { 0x40f3, 0x0000} ++ }, { ++ 61333000, { ++ { 0x0072, 0x0001 }, ++ { 0x2142, 0x0001 }, ++ { 0x40a2, 0x0001 }, + }, +- }, { +- 54000000, { +- { 0x0072, 0x0001}, +- { 0x2142, 0x0001}, +- { 0x40a2, 0x0001}, ++ }, { ++ 73600000, { ++ { 0x0072, 0x0001 }, ++ { 0x2142, 0x0001 }, ++ { 0x4061, 0x0002 }, + }, +- }, { +- 65000000, { +- { 0x0072, 0x0001}, +- { 0x2142, 0x0001}, +- { 0x40a2, 0x0001}, ++ }, { ++ 92000000, { ++ { 0x0072, 0x0001 }, ++ { 0x2145, 0x0002 }, ++ { 0x4061, 0x0002 }, + }, +- }, { +- 66000000, { +- { 0x013e, 0x0003}, +- { 0x217e, 0x0002}, +- { 0x4061, 0x0002} ++ }, { ++ 122666000, { ++ { 0x0051, 0x0002 }, ++ { 0x2145, 0x0002 }, ++ { 0x4061, 0x0002 }, + }, +- }, { +- 74250000, { +- { 0x0072, 0x0001}, +- { 0x2145, 0x0002}, +- { 0x4061, 0x0002} ++ }, { ++ 147200000, { ++ { 0x0051, 0x0002 }, ++ { 0x2145, 0x0002 }, ++ { 0x4064, 0x0003 }, + }, +- }, { +- 83500000, { +- { 0x0072, 0x0001}, ++ }, { ++ 184000000, { ++ { 0x0051, 0x0002 }, ++ { 0x214c, 0x0003 }, ++ { 0x4064, 0x0003 }, + }, +- }, { +- 108000000, { +- { 0x0051, 0x0002}, +- { 0x2145, 0x0002}, +- { 0x4061, 0x0002} ++ }, { ++ 226666000, { ++ { 0x0040, 0x0003 }, ++ { 0x214c, 0x0003 }, ++ { 0x4064, 0x0003 }, + }, +- }, { +- 106500000, { +- { 0x0051, 0x0002}, +- { 0x2145, 0x0002}, +- { 0x4061, 0x0002} ++ }, { ++ 272000000, { ++ { 0x0040, 0x0003 }, ++ { 0x214c, 0x0003 }, ++ { 0x5a64, 0x0003 }, + }, +- }, { +- 146250000, { +- { 0x0051, 0x0002}, +- { 0x2145, 0x0002}, +- { 0x4061, 0x0002} ++ }, { ++ 340000000, { ++ { 0x0040, 0x0003 }, ++ { 0x3b4c, 0x0003 }, ++ { 0x5a64, 0x0003 }, + }, +- }, { +- 148500000, { +- { 0x0051, 0x0003}, +- { 0x214c, 0x0003}, +- { 0x4064, 0x0003} ++ }, { ++ 600000000, { ++ { 0x1a40, 0x0003 }, ++ { 0x3b4c, 0x0003 }, ++ { 0x5a64, 0x0003 }, + }, +- }, { ++ }, { + ~0UL, { +- { 0x00a0, 0x000a }, +- { 0x2001, 0x000f }, +- { 0x4002, 0x000f }, ++ { 0x0000, 0x0000 }, ++ { 0x0000, 0x0000 }, ++ { 0x0000, 0x0000 }, + }, + } + }; +@@ -160,20 +181,8 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = { + static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = { + /* pixelclk bpp8 bpp10 bpp12 */ + { +- 40000000, { 0x0018, 0x0018, 0x0018 }, +- }, { +- 65000000, { 0x0028, 0x0028, 0x0028 }, +- }, { +- 66000000, { 0x0038, 0x0038, 0x0038 }, +- }, { +- 74250000, { 0x0028, 0x0038, 0x0038 }, +- }, { +- 83500000, { 0x0028, 0x0038, 0x0038 }, +- }, { +- 146250000, { 0x0038, 0x0038, 0x0038 }, +- }, { +- 148500000, { 0x0000, 0x0038, 0x0038 }, +- }, { ++ 600000000, { 0x0000, 0x0000, 0x0000 }, ++ }, { + ~0UL, { 0x0000, 0x0000, 0x0000}, + } + }; +@@ -183,6 +192,7 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = { + { 74250000, 0x8009, 0x0004, 0x0272}, + { 148500000, 0x802b, 0x0004, 0x028d}, + { 297000000, 0x8039, 0x0005, 0x028d}, ++ { 594000000, 0x8039, 0x0000, 0x019d}, + { ~0UL, 0x0000, 0x0000, 0x0000} + }; + +@@ -196,14 +206,17 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + return PTR_ERR(hdmi->regmap); + } + +- hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll"); +- if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) { +- hdmi->vpll_clk = NULL; +- } else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) { ++ hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref"); ++ if (PTR_ERR(hdmi->ref_clk) == -ENOENT) ++ hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll"); ++ ++ if (PTR_ERR(hdmi->ref_clk) == -ENOENT) { ++ hdmi->ref_clk = NULL; ++ } else if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; +- } else if (IS_ERR(hdmi->vpll_clk)) { +- DRM_DEV_ERROR(hdmi->dev, "failed to get vpll clock\n"); +- return PTR_ERR(hdmi->vpll_clk); ++ } else if (IS_ERR(hdmi->ref_clk)) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to get reference clock\n"); ++ return PTR_ERR(hdmi->ref_clk); + } + + hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf"); +@@ -216,27 +229,23 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + return PTR_ERR(hdmi->grf_clk); + } + +- return 0; +-} +- +-static enum drm_mode_status +-dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data, +- const struct drm_display_info *info, +- const struct drm_display_mode *mode) +-{ +- const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg; +- int pclk = mode->clock * 1000; +- bool valid = false; +- int i; +- +- for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) { +- if (pclk == mpll_cfg[i].mpixelclock) { +- valid = true; +- break; +- } ++ hdmi->hclk_clk = devm_clk_get_optional(hdmi->dev, "hclk"); ++ if (PTR_ERR(hdmi->hclk_clk) == -EPROBE_DEFER) { ++ return -EPROBE_DEFER; ++ } else if (IS_ERR(hdmi->hclk_clk)) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to get hclk_clk clock\n"); ++ return PTR_ERR(hdmi->hclk_clk); + } + +- return (valid) ? MODE_OK : MODE_BAD; ++ hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9"); ++ if (IS_ERR(hdmi->avdd_0v9)) ++ return PTR_ERR(hdmi->avdd_0v9); ++ ++ hdmi->avdd_1v8 = devm_regulator_get(hdmi->dev, "avdd-1v8"); ++ if (IS_ERR(hdmi->avdd_1v8)) ++ return PTR_ERR(hdmi->avdd_1v8); ++ ++ return 0; + } + + static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) +@@ -257,7 +266,7 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, + { + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); + +- clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000); ++ clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000); + } + + static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) +@@ -404,7 +413,6 @@ static struct rockchip_hdmi_chip_data rk3228_chip_data = { + }; + + static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = { +- .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, + .phy_config = rockchip_phy_config, +@@ -421,7 +429,6 @@ static struct rockchip_hdmi_chip_data rk3288_chip_data = { + }; + + static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = { +- .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, + .phy_config = rockchip_phy_config, +@@ -441,7 +448,6 @@ static struct rockchip_hdmi_chip_data rk3328_chip_data = { + }; + + static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = { +- .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, + .phy_config = rockchip_phy_config, +@@ -459,7 +465,6 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = { + }; + + static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = { +- .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, + .phy_config = rockchip_phy_config, +@@ -467,6 +472,18 @@ static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = { + .use_drm_infoframe = true, + }; + ++static struct rockchip_hdmi_chip_data rk3568_chip_data = { ++ .lcdsel_grf_reg = -1, ++}; ++ ++static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = { ++ .mpll_cfg = rockchip_mpll_cfg, ++ .cur_ctr = rockchip_cur_ctr, ++ .phy_config = rockchip_phy_config, ++ .phy_data = &rk3568_chip_data, ++ .use_drm_infoframe = true, ++}; ++ + static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3228-dw-hdmi", + .data = &rk3228_hdmi_drv_data +@@ -480,6 +497,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3399-dw-hdmi", + .data = &rk3399_hdmi_drv_data + }, ++ { .compatible = "rockchip,rk3568-dw-hdmi", ++ .data = &rk3568_hdmi_drv_data ++ }, + {}, + }; + MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids); +@@ -511,9 +531,12 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + hdmi->dev = &pdev->dev; + hdmi->chip_data = plat_data->phy_data; + plat_data->phy_data = hdmi; +- encoder = &hdmi->encoder; ++ encoder = &hdmi->encoder.encoder; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); ++ ++ hdmi->encoder.port = of_graph_get_port_by_id(dev->of_node, 0); ++ + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has +@@ -537,11 +560,38 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + return ret; + } + +- ret = clk_prepare_enable(hdmi->vpll_clk); ++ ret = regulator_enable(hdmi->avdd_0v9); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret); ++ goto err_avdd_0v9; ++ } ++ ++ ret = regulator_enable(hdmi->avdd_1v8); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret); ++ goto err_avdd_1v8; ++ } ++ ++ ret = clk_prepare_enable(hdmi->ref_clk); + if (ret) { +- DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ++ DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n", + ret); +- return ret; ++ goto err_clk; ++ } ++ ++ ret = clk_prepare_enable(hdmi->hclk_clk); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI hclk clock: %d\n", ++ ret); ++ goto err_clk; ++ } ++ ++ if (hdmi->chip_data == &rk3568_chip_data) { ++ regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1, ++ HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK | ++ RK3568_HDMI_SCLIN_MSK, ++ RK3568_HDMI_SDAIN_MSK | ++ RK3568_HDMI_SCLIN_MSK)); + } + + drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); +@@ -557,10 +607,19 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + */ + if (IS_ERR(hdmi->hdmi)) { + ret = PTR_ERR(hdmi->hdmi); +- drm_encoder_cleanup(encoder); +- clk_disable_unprepare(hdmi->vpll_clk); ++ goto err_bind; + } + ++ return 0; ++ ++err_bind: ++ clk_disable_unprepare(hdmi->ref_clk); ++ drm_encoder_cleanup(encoder); ++err_clk: ++ regulator_disable(hdmi->avdd_1v8); ++err_avdd_1v8: ++ regulator_disable(hdmi->avdd_0v9); ++err_avdd_0v9: + return ret; + } + +@@ -570,7 +629,10 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, + struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + + dw_hdmi_unbind(hdmi->hdmi); +- clk_disable_unprepare(hdmi->vpll_clk); ++ clk_disable_unprepare(hdmi->ref_clk); ++ ++ regulator_disable(hdmi->avdd_1v8); ++ regulator_disable(hdmi->avdd_0v9); + } + + static const struct component_ops dw_hdmi_rockchip_ops = { +diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c +index 046e8ec2a71c..0a4f72021d6a 100644 +--- a/drivers/gpu/drm/rockchip/inno_hdmi.c ++++ b/drivers/gpu/drm/rockchip/inno_hdmi.c +@@ -26,8 +26,6 @@ + + #include "inno_hdmi.h" + +-#define to_inno_hdmi(x) container_of(x, struct inno_hdmi, x) +- + struct hdmi_data_info { + int vic; + bool sink_is_hdmi; +@@ -56,7 +54,7 @@ struct inno_hdmi { + void __iomem *regs; + + struct drm_connector connector; +- struct drm_encoder encoder; ++ struct rockchip_encoder encoder; + + struct inno_hdmi_i2c *i2c; + struct i2c_adapter *ddc; +@@ -67,6 +65,18 @@ struct inno_hdmi { + struct drm_display_mode previous_mode; + }; + ++static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder) ++{ ++ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); ++ ++ return container_of(rkencoder, struct inno_hdmi, encoder); ++} ++ ++static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector) ++{ ++ return container_of(connector, struct inno_hdmi, connector); ++} ++ + enum { + CSC_ITU601_16_235_TO_RGB_0_255_8BIT, + CSC_ITU601_0_255_TO_RGB_0_255_8BIT, +@@ -483,7 +493,7 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) + { +- struct inno_hdmi *hdmi = to_inno_hdmi(encoder); ++ struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder); + + inno_hdmi_setup(hdmi, adj_mode); + +@@ -493,14 +503,14 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder, + + static void inno_hdmi_encoder_enable(struct drm_encoder *encoder) + { +- struct inno_hdmi *hdmi = to_inno_hdmi(encoder); ++ struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder); + + inno_hdmi_set_pwr_mode(hdmi, NORMAL); + } + + static void inno_hdmi_encoder_disable(struct drm_encoder *encoder) + { +- struct inno_hdmi *hdmi = to_inno_hdmi(encoder); ++ struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder); + + inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR); + } +@@ -536,7 +546,7 @@ static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = { + static enum drm_connector_status + inno_hdmi_connector_detect(struct drm_connector *connector, bool force) + { +- struct inno_hdmi *hdmi = to_inno_hdmi(connector); ++ struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector); + + return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ? + connector_status_connected : connector_status_disconnected; +@@ -544,7 +554,7 @@ inno_hdmi_connector_detect(struct drm_connector *connector, bool force) + + static int inno_hdmi_connector_get_modes(struct drm_connector *connector) + { +- struct inno_hdmi *hdmi = to_inno_hdmi(connector); ++ struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector); + struct edid *edid; + int ret = 0; + +@@ -599,7 +609,7 @@ static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = { + + static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi) + { +- struct drm_encoder *encoder = &hdmi->encoder; ++ struct drm_encoder *encoder = &hdmi->encoder.encoder; + struct device *dev = hdmi->dev; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); +@@ -879,7 +889,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, + return 0; + err_cleanup_hdmi: + hdmi->connector.funcs->destroy(&hdmi->connector); +- hdmi->encoder.funcs->destroy(&hdmi->encoder); ++ hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); + err_put_adapter: + i2c_put_adapter(hdmi->ddc); + err_disable_clk: +@@ -893,7 +903,7 @@ static void inno_hdmi_unbind(struct device *dev, struct device *master, + struct inno_hdmi *hdmi = dev_get_drvdata(dev); + + hdmi->connector.funcs->destroy(&hdmi->connector); +- hdmi->encoder.funcs->destroy(&hdmi->encoder); ++ hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); + + i2c_put_adapter(hdmi->ddc); + clk_disable_unprepare(hdmi->pclk); +diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c +index 1c546c3a8998..319240c33dcc 100644 +--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c ++++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c +@@ -47,7 +47,7 @@ struct rk3066_hdmi { + void __iomem *regs; + + struct drm_connector connector; +- struct drm_encoder encoder; ++ struct rockchip_encoder encoder; + + struct rk3066_hdmi_i2c *i2c; + struct i2c_adapter *ddc; +@@ -58,7 +58,17 @@ struct rk3066_hdmi { + struct drm_display_mode previous_mode; + }; + +-#define to_rk3066_hdmi(x) container_of(x, struct rk3066_hdmi, x) ++static struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder) ++{ ++ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); ++ ++ return container_of(rkencoder, struct rk3066_hdmi, encoder); ++} ++ ++static struct rk3066_hdmi *connector_to_rk3066_hdmi(struct drm_connector *connector) ++{ ++ return container_of(connector, struct rk3066_hdmi, connector); ++} + + static inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset) + { +@@ -380,7 +390,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) + { +- struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder); ++ struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); + + /* Store the display mode for plugin/DPMS poweron events. */ + memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode)); +@@ -388,7 +398,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder, + + static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder) + { +- struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder); ++ struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); + int mux, val; + + mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); +@@ -407,7 +417,7 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder) + + static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder) + { +- struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder); ++ struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); + + DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n"); + +@@ -455,7 +465,7 @@ struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = { + static enum drm_connector_status + rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force) + { +- struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector); ++ struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); + + return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ? + connector_status_connected : connector_status_disconnected; +@@ -463,7 +473,7 @@ rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force) + + static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector) + { +- struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector); ++ struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); + struct edid *edid; + int ret = 0; + +@@ -496,9 +506,9 @@ rk3066_hdmi_connector_mode_valid(struct drm_connector *connector, + static struct drm_encoder * + rk3066_hdmi_connector_best_encoder(struct drm_connector *connector) + { +- struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector); ++ struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); + +- return &hdmi->encoder; ++ return &hdmi->encoder.encoder; + } + + static int +@@ -538,7 +548,7 @@ struct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs = { + static int + rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi) + { +- struct drm_encoder *encoder = &hdmi->encoder; ++ struct drm_encoder *encoder = &hdmi->encoder.encoder; + struct device *dev = hdmi->dev; + + encoder->possible_crtcs = +@@ -816,7 +826,7 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master, + + err_cleanup_hdmi: + hdmi->connector.funcs->destroy(&hdmi->connector); +- hdmi->encoder.funcs->destroy(&hdmi->encoder); ++ hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); + err_disable_i2c: + i2c_put_adapter(hdmi->ddc); + err_disable_hclk: +@@ -831,7 +841,7 @@ static void rk3066_hdmi_unbind(struct device *dev, struct device *master, + struct rk3066_hdmi *hdmi = dev_get_drvdata(dev); + + hdmi->connector.funcs->destroy(&hdmi->connector); +- hdmi->encoder.funcs->destroy(&hdmi->encoder); ++ hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); + + i2c_put_adapter(hdmi->ddc); + clk_disable_unprepare(hdmi->hclk); +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +index e4ebe60b3cc1..2bd9acb265e5 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +@@ -473,7 +473,8 @@ static int __init rockchip_drm_init(void) + int ret; + + num_rockchip_sub_drivers = 0; +- ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP); ++ ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP); ++ ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2); + ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver, + CONFIG_ROCKCHIP_LVDS); + ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver, +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +index aa0909e8edf9..6e3ec9d8f250 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +@@ -18,7 +18,7 @@ + + #define ROCKCHIP_MAX_FB_BUFFER 3 + #define ROCKCHIP_MAX_CONNECTOR 2 +-#define ROCKCHIP_MAX_CRTC 2 ++#define ROCKCHIP_MAX_CRTC 4 + + struct drm_device; + struct drm_connector; +@@ -31,6 +31,9 @@ struct rockchip_crtc_state { + int output_bpc; + int output_flags; + bool enable_afbc; ++ u32 bus_format; ++ u32 bus_flags; ++ int color_space; + }; + #define to_rockchip_crtc_state(s) \ + container_of(s, struct rockchip_crtc_state, base) +@@ -65,4 +68,16 @@ extern struct platform_driver rockchip_dp_driver; + extern struct platform_driver rockchip_lvds_driver; + extern struct platform_driver vop_platform_driver; + extern struct platform_driver rk3066_hdmi_driver; ++extern struct platform_driver vop2_platform_driver; ++ ++struct rockchip_encoder { ++ struct device_node *port; ++ struct drm_encoder encoder; ++}; ++ ++static inline struct rockchip_encoder *to_rockchip_encoder(struct drm_encoder *encoder) ++{ ++ return container_of(encoder, struct rockchip_encoder, encoder); ++} ++ + #endif /* _ROCKCHIP_DRM_DRV_H_ */ +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +index 3aa37e177667..0d2cb4f3922b 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +@@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev) + + dev->mode_config.funcs = &rockchip_drm_mode_config_funcs; + dev->mode_config.helper_private = &rockchip_mode_config_helpers; ++ ++ dev->mode_config.normalize_zpos = true; + } +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +index 857d97cdc67c..1e364d7b50e6 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +@@ -54,9 +54,23 @@ struct vop_afbc { + struct vop_reg enable; + struct vop_reg win_sel; + struct vop_reg format; ++ struct vop_reg rb_swap; ++ struct vop_reg uv_swap; ++ struct vop_reg auto_gating_en; ++ struct vop_reg block_split_en; ++ struct vop_reg pic_vir_width; ++ struct vop_reg tile_num; + struct vop_reg hreg_block_split; ++ struct vop_reg pic_offset; + struct vop_reg pic_size; ++ struct vop_reg dsp_offset; ++ struct vop_reg transform_offset; + struct vop_reg hdr_ptr; ++ struct vop_reg half_block_en; ++ struct vop_reg xmirror; ++ struct vop_reg ymirror; ++ struct vop_reg rotate_270; ++ struct vop_reg rotate_90; + struct vop_reg rstn; + }; + +@@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv) + } + + extern const struct component_ops vop_component_ops; ++ + #endif /* _ROCKCHIP_DRM_VOP_H */ +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +new file mode 100644 +index 000000000000..394dd6c58368 +--- /dev/null ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +@@ -0,0 +1,2708 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright (c) 2020 Rockchip Electronics Co., Ltd. ++ * Author: Andy Yan ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "rockchip_drm_drv.h" ++#include "rockchip_drm_gem.h" ++#include "rockchip_drm_fb.h" ++#include "rockchip_drm_vop2.h" ++ ++/* ++ * VOP2 architecture ++ * ++ +----------+ +-------------+ +-----------+ ++ | Cluster | | Sel 1 from 6| | 1 from 3 | ++ | window0 | | Layer0 | | RGB | ++ +----------+ +-------------+ +---------------+ +-------------+ +-----------+ ++ +----------+ +-------------+ |N from 6 layers| | | ++ | Cluster | | Sel 1 from 6| | Overlay0 +--->| Video Port0 | +-----------+ ++ | window1 | | Layer1 | | | | | | 1 from 3 | ++ +----------+ +-------------+ +---------------+ +-------------+ | LVDS | ++ +----------+ +-------------+ +-----------+ ++ | Esmart | | Sel 1 from 6| ++ | window0 | | Layer2 | +---------------+ +-------------+ +-----------+ ++ +----------+ +-------------+ |N from 6 Layers| | | +--> | 1 from 3 | ++ +----------+ +-------------+ --------> | Overlay1 +--->| Video Port1 | | MIPI | ++ | Esmart | | Sel 1 from 6| --------> | | | | +-----------+ ++ | Window1 | | Layer3 | +---------------+ +-------------+ ++ +----------+ +-------------+ +-----------+ ++ +----------+ +-------------+ | 1 from 3 | ++ | Smart | | Sel 1 from 6| +---------------+ +-------------+ | HDMI | ++ | Window0 | | Layer4 | |N from 6 Layers| | | +-----------+ ++ +----------+ +-------------+ | Overlay2 +--->| Video Port2 | ++ +----------+ +-------------+ | | | | +-----------+ ++ | Smart | | Sel 1 from 6| +---------------+ +-------------+ | 1 from 3 | ++ | Window1 | | Layer5 | | eDP | ++ +----------+ +-------------+ +-----------+ ++ * ++ */ ++ ++enum vop2_data_format { ++ VOP2_FMT_ARGB8888 = 0, ++ VOP2_FMT_RGB888, ++ VOP2_FMT_RGB565, ++ VOP2_FMT_XRGB101010, ++ VOP2_FMT_YUV420SP, ++ VOP2_FMT_YUV422SP, ++ VOP2_FMT_YUV444SP, ++ VOP2_FMT_YUYV422 = 8, ++ VOP2_FMT_YUYV420, ++ VOP2_FMT_VYUY422, ++ VOP2_FMT_VYUY420, ++ VOP2_FMT_YUV420SP_TILE_8x4 = 0x10, ++ VOP2_FMT_YUV420SP_TILE_16x2, ++ VOP2_FMT_YUV422SP_TILE_8x4, ++ VOP2_FMT_YUV422SP_TILE_16x2, ++ VOP2_FMT_YUV420SP_10, ++ VOP2_FMT_YUV422SP_10, ++ VOP2_FMT_YUV444SP_10, ++}; ++ ++enum vop2_afbc_format { ++ VOP2_AFBC_FMT_RGB565, ++ VOP2_AFBC_FMT_ARGB2101010 = 2, ++ VOP2_AFBC_FMT_YUV420_10BIT, ++ VOP2_AFBC_FMT_RGB888, ++ VOP2_AFBC_FMT_ARGB8888, ++ VOP2_AFBC_FMT_YUV420 = 9, ++ VOP2_AFBC_FMT_YUV422 = 0xb, ++ VOP2_AFBC_FMT_YUV422_10BIT = 0xe, ++ VOP2_AFBC_FMT_INVALID = -1, ++}; ++ ++union vop2_alpha_ctrl { ++ u32 val; ++ struct { ++ /* [0:1] */ ++ u32 color_mode:1; ++ u32 alpha_mode:1; ++ /* [2:3] */ ++ u32 blend_mode:2; ++ u32 alpha_cal_mode:1; ++ /* [5:7] */ ++ u32 factor_mode:3; ++ /* [8:9] */ ++ u32 alpha_en:1; ++ u32 src_dst_swap:1; ++ u32 reserved:6; ++ /* [16:23] */ ++ u32 glb_alpha:8; ++ } bits; ++}; ++ ++struct vop2_alpha { ++ union vop2_alpha_ctrl src_color_ctrl; ++ union vop2_alpha_ctrl dst_color_ctrl; ++ union vop2_alpha_ctrl src_alpha_ctrl; ++ union vop2_alpha_ctrl dst_alpha_ctrl; ++}; ++ ++struct vop2_alpha_config { ++ bool src_premulti_en; ++ bool dst_premulti_en; ++ bool src_pixel_alpha_en; ++ bool dst_pixel_alpha_en; ++ u16 src_glb_alpha_value; ++ u16 dst_glb_alpha_value; ++}; ++ ++struct vop2_win { ++ struct vop2 *vop2; ++ struct drm_plane base; ++ const struct vop2_win_data *data; ++ struct regmap_field *reg[VOP2_WIN_MAX_REG]; ++ ++ /** ++ * @win_id: graphic window id, a cluster may be split into two ++ * graphics windows. ++ */ ++ u8 win_id; ++ u8 delay; ++ u32 offset; ++ ++ enum drm_plane_type type; ++}; ++ ++struct vop2_video_port { ++ struct drm_crtc crtc; ++ struct vop2 *vop2; ++ struct clk *dclk; ++ unsigned int id; ++ const struct vop2_video_port_regs *regs; ++ const struct vop2_video_port_data *data; ++ ++ struct completion dsp_hold_completion; ++ ++ /** ++ * @win_mask: Bitmask of windows attached to the video port; ++ */ ++ u32 win_mask; ++ ++ struct vop2_win *primary_plane; ++ struct drm_pending_vblank_event *event; ++ ++ unsigned int nlayers; ++}; ++ ++struct vop2 { ++ struct device *dev; ++ struct drm_device *drm; ++ struct vop2_video_port vps[ROCKCHIP_MAX_CRTC]; ++ ++ const struct vop2_data *data; ++ /* ++ * Number of windows that are registered as plane, may be less than the ++ * total number of hardware windows. ++ */ ++ u32 registered_num_wins; ++ ++ void __iomem *regs; ++ struct regmap *map; ++ ++ struct regmap *grf; ++ ++ /* physical map length of vop2 register */ ++ u32 len; ++ ++ void __iomem *lut_regs; ++ ++ /* protects crtc enable/disable */ ++ struct mutex vop2_lock; ++ ++ int irq; ++ ++ /* ++ * Some global resources are shared between all video ports(crtcs), so ++ * we need a ref counter here. ++ */ ++ unsigned int enable_count; ++ struct clk *hclk; ++ struct clk *aclk; ++ ++ /* must be put at the end of the struct */ ++ struct vop2_win win[]; ++}; ++ ++static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc) ++{ ++ return container_of(crtc, struct vop2_video_port, crtc); ++} ++ ++static struct vop2_win *to_vop2_win(struct drm_plane *p) ++{ ++ return container_of(p, struct vop2_win, base); ++} ++ ++static void vop2_lock(struct vop2 *vop2) ++{ ++ mutex_lock(&vop2->vop2_lock); ++} ++ ++static void vop2_unlock(struct vop2 *vop2) ++{ ++ mutex_unlock(&vop2->vop2_lock); ++} ++ ++static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v) ++{ ++ regmap_write(vop2->map, offset, v); ++} ++ ++static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v) ++{ ++ regmap_write(vp->vop2->map, vp->data->offset + offset, v); ++} ++ ++static u32 vop2_readl(struct vop2 *vop2, u32 offset) ++{ ++ u32 val; ++ ++ regmap_read(vop2->map, offset, &val); ++ ++ return val; ++} ++ ++static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v) ++{ ++ regmap_field_write(win->reg[reg], v); ++} ++ ++static bool vop2_cluster_window(const struct vop2_win *win) ++{ ++ return win->data->feature & WIN_FEATURE_CLUSTER; ++} ++ ++static void vop2_cfg_done(struct vop2_video_port *vp) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ ++ regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE, ++ BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN); ++} ++ ++static void vop2_win_disable(struct vop2_win *win) ++{ ++ vop2_win_write(win, VOP2_WIN_ENABLE, 0); ++ ++ if (vop2_cluster_window(win)) ++ vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0); ++} ++ ++static enum vop2_data_format vop2_convert_format(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_XRGB8888: ++ case DRM_FORMAT_ARGB8888: ++ case DRM_FORMAT_XBGR8888: ++ case DRM_FORMAT_ABGR8888: ++ return VOP2_FMT_ARGB8888; ++ case DRM_FORMAT_RGB888: ++ case DRM_FORMAT_BGR888: ++ return VOP2_FMT_RGB888; ++ case DRM_FORMAT_RGB565: ++ case DRM_FORMAT_BGR565: ++ return VOP2_FMT_RGB565; ++ case DRM_FORMAT_NV12: ++ return VOP2_FMT_YUV420SP; ++ case DRM_FORMAT_NV16: ++ return VOP2_FMT_YUV422SP; ++ case DRM_FORMAT_NV24: ++ return VOP2_FMT_YUV444SP; ++ case DRM_FORMAT_YUYV: ++ case DRM_FORMAT_YVYU: ++ return VOP2_FMT_VYUY422; ++ case DRM_FORMAT_VYUY: ++ case DRM_FORMAT_UYVY: ++ return VOP2_FMT_YUYV422; ++ default: ++ DRM_ERROR("unsupported format[%08x]\n", format); ++ return -EINVAL; ++ } ++} ++ ++static enum vop2_afbc_format vop2_convert_afbc_format(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_XRGB8888: ++ case DRM_FORMAT_ARGB8888: ++ case DRM_FORMAT_XBGR8888: ++ case DRM_FORMAT_ABGR8888: ++ return VOP2_AFBC_FMT_ARGB8888; ++ case DRM_FORMAT_RGB888: ++ case DRM_FORMAT_BGR888: ++ return VOP2_AFBC_FMT_RGB888; ++ case DRM_FORMAT_RGB565: ++ case DRM_FORMAT_BGR565: ++ return VOP2_AFBC_FMT_RGB565; ++ case DRM_FORMAT_NV12: ++ return VOP2_AFBC_FMT_YUV420; ++ case DRM_FORMAT_NV16: ++ return VOP2_AFBC_FMT_YUV422; ++ default: ++ return VOP2_AFBC_FMT_INVALID; ++ } ++ ++ return VOP2_AFBC_FMT_INVALID; ++} ++ ++static bool vop2_win_rb_swap(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_XBGR8888: ++ case DRM_FORMAT_ABGR8888: ++ case DRM_FORMAT_BGR888: ++ case DRM_FORMAT_BGR565: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool vop2_afbc_rb_swap(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_NV24: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool vop2_afbc_uv_swap(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_NV12: ++ case DRM_FORMAT_NV16: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool vop2_win_uv_swap(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_NV12: ++ case DRM_FORMAT_NV16: ++ case DRM_FORMAT_NV24: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool vop2_win_dither_up(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_BGR565: ++ case DRM_FORMAT_RGB565: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode) ++{ ++ /* ++ * FIXME: ++ * ++ * There is no media type for YUV444 output, ++ * so when out_mode is AAAA or P888, assume output is YUV444 on ++ * yuv format. ++ * ++ * From H/W testing, YUV444 mode need a rb swap. ++ */ ++ if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 || ++ bus_format == MEDIA_BUS_FMT_VYUY8_1X16 || ++ bus_format == MEDIA_BUS_FMT_YVYU8_2X8 || ++ bus_format == MEDIA_BUS_FMT_VYUY8_2X8 || ++ ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 || ++ bus_format == MEDIA_BUS_FMT_YUV10_1X30) && ++ (output_mode == ROCKCHIP_OUT_MODE_AAAA || ++ output_mode == ROCKCHIP_OUT_MODE_P888))) ++ return true; ++ else ++ return false; ++} ++ ++static bool is_yuv_output(u32 bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ case MEDIA_BUS_FMT_YUYV8_2X8: ++ case MEDIA_BUS_FMT_YVYU8_2X8: ++ case MEDIA_BUS_FMT_UYVY8_2X8: ++ case MEDIA_BUS_FMT_VYUY8_2X8: ++ case MEDIA_BUS_FMT_YUYV8_1X16: ++ case MEDIA_BUS_FMT_YVYU8_1X16: ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_VYUY8_1X16: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool rockchip_afbc(struct drm_plane *plane, u64 modifier) ++{ ++ int i; ++ ++ if (modifier == DRM_FORMAT_MOD_LINEAR) ++ return false; ++ ++ for (i = 0 ; i < plane->modifier_count; i++) ++ if (plane->modifiers[i] == modifier) ++ return true; ++ ++ return false; ++ ++} ++ ++static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format, ++ u64 modifier) ++{ ++ struct vop2_win *win = to_vop2_win(plane); ++ struct vop2 *vop2 = win->vop2; ++ ++ if (modifier == DRM_FORMAT_MOD_INVALID) ++ return false; ++ ++ if (modifier == DRM_FORMAT_MOD_LINEAR) ++ return true; ++ ++ if (!rockchip_afbc(plane, modifier)) { ++ drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n", ++ modifier); ++ ++ return false; ++ } ++ ++ return vop2_convert_afbc_format(format) >= 0; ++} ++ ++static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate, ++ bool afbc_half_block_en) ++{ ++ struct drm_rect *src = &pstate->src; ++ struct drm_framebuffer *fb = pstate->fb; ++ u32 bpp = fb->format->cpp[0] * 8; ++ u32 vir_width = (fb->pitches[0] << 3) / bpp; ++ u32 width = drm_rect_width(src) >> 16; ++ u32 height = drm_rect_height(src) >> 16; ++ u32 act_xoffset = src->x1 >> 16; ++ u32 act_yoffset = src->y1 >> 16; ++ u32 align16_crop = 0; ++ u32 align64_crop = 0; ++ u32 height_tmp; ++ u8 tx, ty; ++ u8 bottom_crop_line_num = 0; ++ ++ /* 16 pixel align */ ++ if (height & 0xf) ++ align16_crop = 16 - (height & 0xf); ++ ++ height_tmp = height + align16_crop; ++ ++ /* 64 pixel align */ ++ if (height_tmp & 0x3f) ++ align64_crop = 64 - (height_tmp & 0x3f); ++ ++ bottom_crop_line_num = align16_crop + align64_crop; ++ ++ switch (pstate->rotation & ++ (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y | ++ DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) { ++ case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y: ++ tx = 16 - ((act_xoffset + width) & 0xf); ++ ty = bottom_crop_line_num - act_yoffset; ++ break; ++ case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90: ++ tx = bottom_crop_line_num - act_yoffset; ++ ty = vir_width - width - act_xoffset; ++ break; ++ case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270: ++ tx = act_yoffset; ++ ty = act_xoffset; ++ break; ++ case DRM_MODE_REFLECT_X: ++ tx = 16 - ((act_xoffset + width) & 0xf); ++ ty = act_yoffset; ++ break; ++ case DRM_MODE_REFLECT_Y: ++ tx = act_xoffset; ++ ty = bottom_crop_line_num - act_yoffset; ++ break; ++ case DRM_MODE_ROTATE_90: ++ tx = bottom_crop_line_num - act_yoffset; ++ ty = act_xoffset; ++ break; ++ case DRM_MODE_ROTATE_270: ++ tx = act_yoffset; ++ ty = vir_width - width - act_xoffset; ++ break; ++ case 0: ++ tx = act_xoffset; ++ ty = act_yoffset; ++ break; ++ } ++ ++ if (afbc_half_block_en) ++ ty &= 0x7f; ++ ++#define TRANSFORM_XOFFSET GENMASK(7, 0) ++#define TRANSFORM_YOFFSET GENMASK(23, 16) ++ return FIELD_PREP(TRANSFORM_XOFFSET, tx) | ++ FIELD_PREP(TRANSFORM_YOFFSET, ty); ++} ++ ++/* ++ * A Cluster window has 2048 x 16 line buffer, which can ++ * works at 2048 x 16(Full) or 4096 x 8 (Half) mode. ++ * for Cluster_lb_mode register: ++ * 0: half mode, for plane input width range 2048 ~ 4096 ++ * 1: half mode, for cluster work at 2 * 2048 plane mode ++ * 2: half mode, for rotate_90/270 mode ++ * ++ */ ++static int vop2_get_cluster_lb_mode(struct vop2_win *win, ++ struct drm_plane_state *pstate) ++{ ++ if ((pstate->rotation & DRM_MODE_ROTATE_270) || ++ (pstate->rotation & DRM_MODE_ROTATE_90)) ++ return 2; ++ else ++ return 0; ++} ++ ++static u16 vop2_scale_factor(u32 src, u32 dst) ++{ ++ u32 fac; ++ int shift; ++ ++ if (src == dst) ++ return 0; ++ ++ if (dst < 2) ++ return U16_MAX; ++ ++ if (src < 2) ++ return 0; ++ ++ if (src > dst) ++ shift = 12; ++ else ++ shift = 16; ++ ++ src--; ++ dst--; ++ ++ fac = DIV_ROUND_UP(src << shift, dst) - 1; ++ ++ if (fac > U16_MAX) ++ return U16_MAX; ++ ++ return fac; ++} ++ ++static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win, ++ u32 src_w, u32 src_h, u32 dst_w, ++ u32 dst_h, u32 pixel_format) ++{ ++ const struct drm_format_info *info; ++ u16 hor_scl_mode, ver_scl_mode; ++ u16 hscl_filter_mode, vscl_filter_mode; ++ u8 gt2 = 0; ++ u8 gt4 = 0; ++ u32 val; ++ ++ info = drm_format_info(pixel_format); ++ ++ if (src_h >= (4 * dst_h)) { ++ gt4 = 1; ++ src_h >>= 2; ++ } else if (src_h >= (2 * dst_h)) { ++ gt2 = 1; ++ src_h >>= 1; ++ } ++ ++ hor_scl_mode = scl_get_scl_mode(src_w, dst_w); ++ ver_scl_mode = scl_get_scl_mode(src_h, dst_h); ++ ++ if (hor_scl_mode == SCALE_UP) ++ hscl_filter_mode = VOP2_SCALE_UP_BIC; ++ else ++ hscl_filter_mode = VOP2_SCALE_DOWN_BIL; ++ ++ if (ver_scl_mode == SCALE_UP) ++ vscl_filter_mode = VOP2_SCALE_UP_BIL; ++ else ++ vscl_filter_mode = VOP2_SCALE_DOWN_BIL; ++ ++ /* ++ * RK3568 VOP Esmart/Smart dsp_w should be even pixel ++ * at scale down mode ++ */ ++ if (!(win->data->feature & WIN_FEATURE_AFBDC)) { ++ if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) { ++ drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n", ++ win->data->name, dst_w); ++ dst_w++; ++ } ++ } ++ ++ val = vop2_scale_factor(src_w, dst_w); ++ vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val); ++ val = vop2_scale_factor(src_h, dst_h); ++ vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val); ++ ++ vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4); ++ vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2); ++ ++ vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode); ++ vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode); ++ ++ if (vop2_cluster_window(win)) ++ return; ++ ++ vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode); ++ vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode); ++ ++ if (info->is_yuv) { ++ src_w /= info->hsub; ++ src_h /= info->vsub; ++ ++ gt4 = gt2 = 0; ++ ++ if (src_h >= (4 * dst_h)) { ++ gt4 = 1; ++ src_h >>= 2; ++ } else if (src_h >= (2 * dst_h)) { ++ gt2 = 1; ++ src_h >>= 1; ++ } ++ ++ hor_scl_mode = scl_get_scl_mode(src_w, dst_w); ++ ver_scl_mode = scl_get_scl_mode(src_h, dst_h); ++ ++ val = vop2_scale_factor(src_w, dst_w); ++ vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val); ++ ++ val = vop2_scale_factor(src_h, dst_h); ++ vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val); ++ ++ vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4); ++ vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2); ++ vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode); ++ vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode); ++ vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode); ++ vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode); ++ } ++} ++ ++static int vop2_convert_csc_mode(int csc_mode) ++{ ++ switch (csc_mode) { ++ case V4L2_COLORSPACE_SMPTE170M: ++ case V4L2_COLORSPACE_470_SYSTEM_M: ++ case V4L2_COLORSPACE_470_SYSTEM_BG: ++ return CSC_BT601L; ++ case V4L2_COLORSPACE_REC709: ++ case V4L2_COLORSPACE_SMPTE240M: ++ case V4L2_COLORSPACE_DEFAULT: ++ return CSC_BT709L; ++ case V4L2_COLORSPACE_JPEG: ++ return CSC_BT601F; ++ case V4L2_COLORSPACE_BT2020: ++ return CSC_BT2020; ++ default: ++ return CSC_BT709L; ++ } ++} ++ ++/* ++ * colorspace path: ++ * Input Win csc Output ++ * 1. YUV(2020) --> Y2R->2020To709->R2Y --> YUV_OUTPUT(601/709) ++ * RGB --> R2Y __/ ++ * ++ * 2. YUV(2020) --> bypasss --> YUV_OUTPUT(2020) ++ * RGB --> 709To2020->R2Y __/ ++ * ++ * 3. YUV(2020) --> Y2R->2020To709 --> RGB_OUTPUT(709) ++ * RGB --> R2Y __/ ++ * ++ * 4. YUV(601/709)-> Y2R->709To2020->R2Y --> YUV_OUTPUT(2020) ++ * RGB --> 709To2020->R2Y __/ ++ * ++ * 5. YUV(601/709)-> bypass --> YUV_OUTPUT(709) ++ * RGB --> R2Y __/ ++ * ++ * 6. YUV(601/709)-> bypass --> YUV_OUTPUT(601) ++ * RGB --> R2Y(601) __/ ++ * ++ * 7. YUV --> Y2R(709) --> RGB_OUTPUT(709) ++ * RGB --> bypass __/ ++ * ++ * 8. RGB --> 709To2020->R2Y --> YUV_OUTPUT(2020) ++ * ++ * 9. RGB --> R2Y(709) --> YUV_OUTPUT(709) ++ * ++ * 10. RGB --> R2Y(601) --> YUV_OUTPUT(601) ++ * ++ * 11. RGB --> bypass --> RGB_OUTPUT(709) ++ */ ++ ++static void vop2_setup_csc_mode(struct vop2_video_port *vp, ++ struct vop2_win *win, ++ struct drm_plane_state *pstate) ++{ ++ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state); ++ int is_input_yuv = pstate->fb->format->is_yuv; ++ int is_output_yuv = is_yuv_output(vcstate->bus_format); ++ int input_csc = V4L2_COLORSPACE_DEFAULT; ++ int output_csc = vcstate->color_space; ++ bool r2y_en, y2r_en; ++ int csc_mode; ++ ++ if (is_input_yuv && !is_output_yuv) { ++ y2r_en = true; ++ r2y_en = false; ++ csc_mode = vop2_convert_csc_mode(input_csc); ++ } else if (!is_input_yuv && is_output_yuv) { ++ y2r_en = false; ++ r2y_en = true; ++ csc_mode = vop2_convert_csc_mode(output_csc); ++ } else { ++ y2r_en = false; ++ r2y_en = false; ++ csc_mode = false; ++ } ++ ++ vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en); ++ vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en); ++ vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode); ++} ++ ++static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ ++ vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq); ++ vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq); ++} ++ ++static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ ++ vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16); ++} ++ ++static int vop2_core_clks_prepare_enable(struct vop2 *vop2) ++{ ++ int ret; ++ ++ ret = clk_prepare_enable(vop2->hclk); ++ if (ret < 0) { ++ drm_err(vop2->drm, "failed to enable hclk - %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(vop2->aclk); ++ if (ret < 0) { ++ drm_err(vop2->drm, "failed to enable aclk - %d\n", ret); ++ goto err; ++ } ++ ++ return 0; ++err: ++ clk_disable_unprepare(vop2->hclk); ++ ++ return ret; ++} ++ ++static void vop2_enable(struct vop2 *vop2) ++{ ++ int ret; ++ ++ ret = pm_runtime_get_sync(vop2->dev); ++ if (ret < 0) { ++ drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret); ++ return; ++ } ++ ++ ret = vop2_core_clks_prepare_enable(vop2); ++ if (ret) { ++ pm_runtime_put_sync(vop2->dev); ++ return; ++ } ++ ++ if (vop2->data->soc_id == 3566) ++ vop2_writel(vop2, RK3568_OTP_WIN_EN, 1); ++ ++ vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN); ++ ++ /* ++ * Disable auto gating, this is a workaround to ++ * avoid display image shift when a window enabled. ++ */ ++ regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL, ++ RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN); ++ ++ vop2_writel(vop2, RK3568_SYS0_INT_CLR, ++ VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR); ++ vop2_writel(vop2, RK3568_SYS0_INT_EN, ++ VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR); ++ vop2_writel(vop2, RK3568_SYS1_INT_CLR, ++ VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR); ++ vop2_writel(vop2, RK3568_SYS1_INT_EN, ++ VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR); ++} ++ ++static void vop2_disable(struct vop2 *vop2) ++{ ++ pm_runtime_put_sync(vop2->dev); ++ ++ clk_disable_unprepare(vop2->aclk); ++ clk_disable_unprepare(vop2->hclk); ++} ++ ++static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, ++ struct drm_atomic_state *state) ++{ ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct vop2 *vop2 = vp->vop2; ++ int ret; ++ ++ vop2_lock(vop2); ++ ++ drm_crtc_vblank_off(crtc); ++ ++ /* ++ * Vop standby will take effect at end of current frame, ++ * if dsp hold valid irq happen, it means standby complete. ++ * ++ * we must wait standby complete when we want to disable aclk, ++ * if not, memory bus maybe dead. ++ */ ++ reinit_completion(&vp->dsp_hold_completion); ++ ++ vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID); ++ ++ vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY); ++ ++ ret = wait_for_completion_timeout(&vp->dsp_hold_completion, ++ msecs_to_jiffies(50)); ++ if (!ret) ++ drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id); ++ ++ vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID); ++ ++ clk_disable_unprepare(vp->dclk); ++ ++ vop2->enable_count--; ++ ++ if (!vop2->enable_count) ++ vop2_disable(vop2); ++ ++ vop2_unlock(vop2); ++ ++ if (crtc->state->event && !crtc->state->active) { ++ spin_lock_irq(&crtc->dev->event_lock); ++ drm_crtc_send_vblank_event(crtc, crtc->state->event); ++ spin_unlock_irq(&crtc->dev->event_lock); ++ ++ crtc->state->event = NULL; ++ } ++} ++ ++static int vop2_plane_atomic_check(struct drm_plane *plane, ++ struct drm_atomic_state *astate) ++{ ++ struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane); ++ struct drm_framebuffer *fb = pstate->fb; ++ struct drm_crtc *crtc = pstate->crtc; ++ struct drm_crtc_state *cstate; ++ struct vop2_video_port *vp; ++ struct vop2 *vop2; ++ const struct vop2_data *vop2_data; ++ struct drm_rect *dest = &pstate->dst; ++ struct drm_rect *src = &pstate->src; ++ int min_scale = FRAC_16_16(1, 8); ++ int max_scale = FRAC_16_16(8, 1); ++ int format; ++ int ret; ++ ++ if (!crtc) ++ return 0; ++ ++ vp = to_vop2_video_port(crtc); ++ vop2 = vp->vop2; ++ vop2_data = vop2->data; ++ ++ cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc); ++ if (WARN_ON(!cstate)) ++ return -EINVAL; ++ ++ ret = drm_atomic_helper_check_plane_state(pstate, cstate, ++ min_scale, max_scale, ++ true, true); ++ if (ret) ++ return ret; ++ ++ if (!pstate->visible) ++ return 0; ++ ++ format = vop2_convert_format(fb->format->format); ++ if (format < 0) ++ return format; ++ ++ if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 || ++ drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) { ++ drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n", ++ drm_rect_width(src) >> 16, drm_rect_height(src) >> 16, ++ drm_rect_width(dest), drm_rect_height(dest)); ++ pstate->visible = false; ++ return 0; ++ } ++ ++ if (drm_rect_width(src) >> 16 > vop2_data->max_input.width || ++ drm_rect_height(src) >> 16 > vop2_data->max_input.height) { ++ drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n", ++ drm_rect_width(src) >> 16, ++ drm_rect_height(src) >> 16, ++ vop2_data->max_input.width, ++ vop2_data->max_input.height); ++ return -EINVAL; ++ } ++ ++ /* ++ * Src.x1 can be odd when do clip, but yuv plane start point ++ * need align with 2 pixel. ++ */ ++ if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) { ++ drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void vop2_plane_atomic_disable(struct drm_plane *plane, ++ struct drm_atomic_state *state) ++{ ++ struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane); ++ struct vop2_win *win = to_vop2_win(plane); ++ struct vop2 *vop2 = win->vop2; ++ ++ drm_dbg(vop2->drm, "%s disable\n", win->data->name); ++ ++ if (!old_pstate->crtc) ++ return; ++ ++ vop2_win_disable(win); ++ vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0); ++} ++ ++/* ++ * The color key is 10 bit, so all format should ++ * convert to 10 bit here. ++ */ ++static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key) ++{ ++ struct drm_plane_state *pstate = plane->state; ++ struct drm_framebuffer *fb = pstate->fb; ++ struct vop2_win *win = to_vop2_win(plane); ++ u32 color_key_en = 0; ++ u32 r = 0; ++ u32 g = 0; ++ u32 b = 0; ++ ++ if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) { ++ vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0); ++ return; ++ } ++ ++ switch (fb->format->format) { ++ case DRM_FORMAT_RGB565: ++ case DRM_FORMAT_BGR565: ++ r = (color_key & 0xf800) >> 11; ++ g = (color_key & 0x7e0) >> 5; ++ b = (color_key & 0x1f); ++ r <<= 5; ++ g <<= 4; ++ b <<= 5; ++ color_key_en = 1; ++ break; ++ case DRM_FORMAT_XRGB8888: ++ case DRM_FORMAT_ARGB8888: ++ case DRM_FORMAT_XBGR8888: ++ case DRM_FORMAT_ABGR8888: ++ case DRM_FORMAT_RGB888: ++ case DRM_FORMAT_BGR888: ++ r = (color_key & 0xff0000) >> 16; ++ g = (color_key & 0xff00) >> 8; ++ b = (color_key & 0xff); ++ r <<= 2; ++ g <<= 2; ++ b <<= 2; ++ color_key_en = 1; ++ break; ++ } ++ ++ vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en); ++ vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b); ++} ++ ++static void vop2_plane_atomic_update(struct drm_plane *plane, ++ struct drm_atomic_state *state) ++{ ++ struct drm_plane_state *pstate = plane->state; ++ struct drm_crtc *crtc = pstate->crtc; ++ struct vop2_win *win = to_vop2_win(plane); ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; ++ struct vop2 *vop2 = win->vop2; ++ struct drm_framebuffer *fb = pstate->fb; ++ u32 bpp = fb->format->cpp[0] * 8; ++ u32 actual_w, actual_h, dsp_w, dsp_h; ++ u32 act_info, dsp_info; ++ u32 format; ++ u32 afbc_format; ++ u32 rb_swap; ++ u32 uv_swap; ++ struct drm_rect *src = &pstate->src; ++ struct drm_rect *dest = &pstate->dst; ++ u32 afbc_tile_num; ++ u32 transform_offset; ++ bool dither_up; ++ bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false; ++ bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false; ++ bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270; ++ bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90; ++ struct rockchip_gem_object *rk_obj; ++ unsigned long offset; ++ bool afbc_en; ++ dma_addr_t yrgb_mst; ++ dma_addr_t uv_mst; ++ ++ /* ++ * can't update plane when vop2 is disabled. ++ */ ++ if (WARN_ON(!crtc)) ++ return; ++ ++ if (!pstate->visible) { ++ vop2_plane_atomic_disable(plane, state); ++ return; ++ } ++ ++ afbc_en = rockchip_afbc(plane, fb->modifier); ++ ++ offset = (src->x1 >> 16) * fb->format->cpp[0]; ++ ++ /* ++ * AFBC HDR_PTR must set to the zero offset of the framebuffer. ++ */ ++ if (afbc_en) ++ offset = 0; ++ else if (pstate->rotation & DRM_MODE_REFLECT_Y) ++ offset += ((src->y2 >> 16) - 1) * fb->pitches[0]; ++ else ++ offset += (src->y1 >> 16) * fb->pitches[0]; ++ ++ rk_obj = to_rockchip_obj(fb->obj[0]); ++ ++ yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0]; ++ if (fb->format->is_yuv) { ++ int hsub = fb->format->hsub; ++ int vsub = fb->format->vsub; ++ ++ offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub; ++ offset += (src->y1 >> 16) * fb->pitches[1] / vsub; ++ ++ if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en) ++ offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub; ++ ++ rk_obj = to_rockchip_obj(fb->obj[0]); ++ uv_mst = rk_obj->dma_addr + offset + fb->offsets[1]; ++ } ++ ++ actual_w = drm_rect_width(src) >> 16; ++ actual_h = drm_rect_height(src) >> 16; ++ dsp_w = drm_rect_width(dest); ++ ++ if (dest->x1 + dsp_w > adjusted_mode->hdisplay) { ++ drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n", ++ vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay); ++ dsp_w = adjusted_mode->hdisplay - dest->x1; ++ if (dsp_w < 4) ++ dsp_w = 4; ++ actual_w = dsp_w * actual_w / drm_rect_width(dest); ++ } ++ ++ dsp_h = drm_rect_height(dest); ++ ++ if (dest->y1 + dsp_h > adjusted_mode->vdisplay) { ++ drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n", ++ vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay); ++ dsp_h = adjusted_mode->vdisplay - dest->y1; ++ if (dsp_h < 4) ++ dsp_h = 4; ++ actual_h = dsp_h * actual_h / drm_rect_height(dest); ++ } ++ ++ /* ++ * This is workaround solution for IC design: ++ * esmart can't support scale down when actual_w % 16 == 1. ++ */ ++ if (!(win->data->feature & WIN_FEATURE_AFBDC)) { ++ if (actual_w > dsp_w && (actual_w & 0xf) == 1) { ++ drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n", ++ vp->id, win->data->name, actual_w); ++ actual_w -= 1; ++ } ++ } ++ ++ if (afbc_en && actual_w % 4) { ++ drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n", ++ vp->id, win->data->name, actual_w); ++ actual_w = ALIGN_DOWN(actual_w, 4); ++ } ++ ++ act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); ++ dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff); ++ ++ format = vop2_convert_format(fb->format->format); ++ ++ drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n", ++ vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h, ++ dest->x1, dest->y1, ++ &fb->format->format, ++ afbc_en ? "AFBC" : "", &yrgb_mst); ++ ++ if (afbc_en) { ++ u32 stride; ++ ++ /* the afbc superblock is 16 x 16 */ ++ afbc_format = vop2_convert_afbc_format(fb->format->format); ++ ++ /* Enable color transform for YTR */ ++ if (fb->modifier & AFBC_FORMAT_MOD_YTR) ++ afbc_format |= (1 << 4); ++ ++ afbc_tile_num = ALIGN(actual_w, 16) >> 4; ++ ++ /* ++ * AFBC pic_vir_width is count by pixel, this is different ++ * with WIN_VIR_STRIDE. ++ */ ++ stride = (fb->pitches[0] << 3) / bpp; ++ if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270)) ++ drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n", ++ vp->id, win->data->name, stride); ++ ++ rb_swap = vop2_afbc_rb_swap(fb->format->format); ++ uv_swap = vop2_afbc_uv_swap(fb->format->format); ++ /* ++ * This is a workaround for crazy IC design, Cluster ++ * and Esmart/Smart use different format configuration map: ++ * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart. ++ * ++ * This is one thing we can make the convert simple: ++ * AFBCD decode all the YUV data to YUV444. So we just ++ * set all the yuv 10 bit to YUV444_10. ++ */ ++ if (fb->format->is_yuv && (bpp == 10)) ++ format = VOP2_CLUSTER_YUV444_10; ++ ++ if (vop2_cluster_window(win)) ++ vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1); ++ vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format); ++ vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap); ++ vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap); ++ vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0); ++ vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0); ++ if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) { ++ vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0); ++ transform_offset = vop2_afbc_transform_offset(pstate, false); ++ } else { ++ vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1); ++ transform_offset = vop2_afbc_transform_offset(pstate, true); ++ } ++ vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst); ++ vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info); ++ vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset); ++ vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1)); ++ vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16))); ++ vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride); ++ vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num); ++ vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror); ++ vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270); ++ vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90); ++ } else { ++ vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4)); ++ } ++ ++ vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror); ++ ++ if (rotate_90 || rotate_270) { ++ act_info = swahw32(act_info); ++ actual_w = drm_rect_height(src) >> 16; ++ actual_h = drm_rect_width(src) >> 16; ++ } ++ ++ vop2_win_write(win, VOP2_WIN_FORMAT, format); ++ vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst); ++ ++ rb_swap = vop2_win_rb_swap(fb->format->format); ++ vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap); ++ if (!vop2_cluster_window(win)) { ++ uv_swap = vop2_win_uv_swap(fb->format->format); ++ vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap); ++ } ++ ++ if (fb->format->is_yuv) { ++ vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4)); ++ vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst); ++ } ++ ++ vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format); ++ if (!vop2_cluster_window(win)) ++ vop2_plane_setup_color_key(plane, 0); ++ vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info); ++ vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info); ++ vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff)); ++ ++ vop2_setup_csc_mode(vp, win, pstate); ++ ++ dither_up = vop2_win_dither_up(fb->format->format); ++ vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up); ++ ++ vop2_win_write(win, VOP2_WIN_ENABLE, 1); ++ ++ if (vop2_cluster_window(win)) { ++ int lb_mode = vop2_get_cluster_lb_mode(win, pstate); ++ ++ vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode); ++ vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1); ++ } ++} ++ ++static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = { ++ .atomic_check = vop2_plane_atomic_check, ++ .atomic_update = vop2_plane_atomic_update, ++ .atomic_disable = vop2_plane_atomic_disable, ++}; ++ ++static const struct drm_plane_funcs vop2_plane_funcs = { ++ .update_plane = drm_atomic_helper_update_plane, ++ .disable_plane = drm_atomic_helper_disable_plane, ++ .destroy = drm_plane_cleanup, ++ .reset = drm_atomic_helper_plane_reset, ++ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, ++ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, ++ .format_mod_supported = rockchip_vop2_mod_supported, ++}; ++ ++static int vop2_crtc_enable_vblank(struct drm_crtc *crtc) ++{ ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ ++ vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD); ++ ++ return 0; ++} ++ ++static void vop2_crtc_disable_vblank(struct drm_crtc *crtc) ++{ ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ ++ vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD); ++} ++ ++static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc, ++ const struct drm_display_mode *mode, ++ struct drm_display_mode *adj_mode) ++{ ++ drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V | ++ CRTC_STEREO_DOUBLE); ++ ++ return true; ++} ++ ++static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl) ++{ ++ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); ++ ++ switch (vcstate->bus_format) { ++ case MEDIA_BUS_FMT_RGB565_1X16: ++ *dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN; ++ break; ++ case MEDIA_BUS_FMT_RGB666_1X18: ++ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: ++ case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: ++ *dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN; ++ *dsp_ctrl |= RGB888_TO_RGB666; ++ break; ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ *dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN; ++ break; ++ default: ++ break; ++ } ++ ++ if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA) ++ *dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN; ++ ++ *dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL, ++ DITHER_DOWN_ALLEGRO); ++} ++ ++static void vop2_post_config(struct drm_crtc *crtc) ++{ ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct drm_display_mode *mode = &crtc->state->adjusted_mode; ++ u16 vtotal = mode->crtc_vtotal; ++ u16 hdisplay = mode->crtc_hdisplay; ++ u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start; ++ u16 vdisplay = mode->crtc_vdisplay; ++ u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start; ++ u32 left_margin = 100, right_margin = 100; ++ u32 top_margin = 100, bottom_margin = 100; ++ u16 hsize = hdisplay * (left_margin + right_margin) / 200; ++ u16 vsize = vdisplay * (top_margin + bottom_margin) / 200; ++ u16 hact_end, vact_end; ++ u32 val; ++ ++ vsize = rounddown(vsize, 2); ++ hsize = rounddown(hsize, 2); ++ hact_st += hdisplay * (100 - left_margin) / 200; ++ hact_end = hact_st + hsize; ++ val = hact_st << 16; ++ val |= hact_end; ++ vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val); ++ vact_st += vdisplay * (100 - top_margin) / 200; ++ vact_end = vact_st + vsize; ++ val = vact_st << 16; ++ val |= vact_end; ++ vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val); ++ val = scl_cal_scale2(vdisplay, vsize) << 16; ++ val |= scl_cal_scale2(hdisplay, hsize); ++ vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val); ++ ++ val = 0; ++ if (hdisplay != hsize) ++ val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN; ++ if (vdisplay != vsize) ++ val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN; ++ vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val); ++ ++ if (mode->flags & DRM_MODE_FLAG_INTERLACE) { ++ u16 vact_st_f1 = vtotal + vact_st + 1; ++ u16 vact_end_f1 = vact_st_f1 + vsize; ++ ++ val = vact_st_f1 << 16 | vact_end_f1; ++ vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val); ++ } ++ ++ vop2_vp_write(vp, RK3568_VP_DSP_BG, 0); ++} ++ ++static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, ++ u32 polflags) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ u32 die, dip; ++ ++ die = vop2_readl(vop2, RK3568_DSP_IF_EN); ++ dip = vop2_readl(vop2, RK3568_DSP_IF_POL); ++ ++ switch (id) { ++ case RK3568_VOP2_EP_RGB: ++ die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX; ++ die |= RK3568_SYS_DSP_INFACE_EN_RGB | ++ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id); ++ if (polflags & POLFLAG_DCLK_INV) ++ regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3)); ++ else ++ regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16)); ++ break; ++ case RK3568_VOP2_EP_HDMI: ++ die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX; ++ die |= RK3568_SYS_DSP_INFACE_EN_HDMI | ++ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id); ++ break; ++ case RK3568_VOP2_EP_EDP: ++ die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX; ++ die |= RK3568_SYS_DSP_INFACE_EN_EDP | ++ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id); ++ break; ++ case RK3568_VOP2_EP_MIPI0: ++ die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX; ++ die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 | ++ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id); ++ dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL; ++ dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags); ++ break; ++ case RK3568_VOP2_EP_MIPI1: ++ die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX; ++ die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 | ++ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id); ++ dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL; ++ dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags); ++ break; ++ case RK3568_VOP2_EP_LVDS0: ++ die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX; ++ die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 | ++ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id); ++ dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; ++ dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); ++ break; ++ case RK3568_VOP2_EP_LVDS1: ++ die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX; ++ die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 | ++ FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id); ++ dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; ++ dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); ++ break; ++ default: ++ return; ++ }; ++ ++ dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD; ++ ++ vop2_writel(vop2, RK3568_DSP_IF_EN, die); ++ vop2_writel(vop2, RK3568_DSP_IF_POL, dip); ++} ++ ++static int us_to_vertical_line(struct drm_display_mode *mode, int us) ++{ ++ return us * mode->clock / mode->htotal / 1000; ++} ++ ++static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, ++ struct drm_atomic_state *state) ++{ ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct vop2 *vop2 = vp->vop2; ++ const struct vop2_data *vop2_data = vop2->data; ++ const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; ++ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); ++ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); ++ struct drm_display_mode *mode = &crtc->state->adjusted_mode; ++ unsigned long clock = mode->crtc_clock * 1000; ++ u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; ++ u16 hdisplay = mode->crtc_hdisplay; ++ u16 htotal = mode->crtc_htotal; ++ u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start; ++ u16 hact_end = hact_st + hdisplay; ++ u16 vdisplay = mode->crtc_vdisplay; ++ u16 vtotal = mode->crtc_vtotal; ++ u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; ++ u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start; ++ u16 vact_end = vact_st + vdisplay; ++ u8 out_mode; ++ u32 dsp_ctrl = 0; ++ int act_end; ++ u32 val, polflags; ++ int ret; ++ struct drm_encoder *encoder; ++ ++ drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n", ++ hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p", ++ drm_mode_vrefresh(mode), vcstate->output_type, vp->id); ++ ++ vop2_lock(vop2); ++ ++ ret = clk_prepare_enable(vp->dclk); ++ if (ret < 0) { ++ drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n", ++ vp->id, ret); ++ return; ++ } ++ ++ if (!vop2->enable_count) ++ vop2_enable(vop2); ++ ++ vop2->enable_count++; ++ ++ vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY); ++ ++ polflags = 0; ++ if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) ++ polflags |= POLFLAG_DCLK_INV; ++ if (mode->flags & DRM_MODE_FLAG_PHSYNC) ++ polflags |= BIT(HSYNC_POSITIVE); ++ if (mode->flags & DRM_MODE_FLAG_PVSYNC) ++ polflags |= BIT(VSYNC_POSITIVE); ++ ++ drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) { ++ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); ++ struct device_node *node, *parent; ++ ++ parent = of_get_parent(rkencoder->port); ++ ++ for_each_endpoint_of_node(parent, node) { ++ struct device_node *crtc_port = of_graph_get_remote_port(node); ++ struct device_node *epn; ++ struct of_endpoint endpoint; ++ ++ if (crtc->port != crtc_port) { ++ of_node_put(crtc_port); ++ continue; ++ } ++ ++ of_node_put(crtc_port); ++ ++ epn = of_graph_get_remote_endpoint(node); ++ of_graph_parse_endpoint(epn, &endpoint); ++ of_node_put(epn); ++ ++ drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n", ++ vp->id, encoder->name, endpoint.id); ++ rk3568_set_intf_mux(vp, endpoint.id, polflags); ++ } ++ of_node_put(parent); ++ } ++ ++ if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA && ++ !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT)) ++ out_mode = ROCKCHIP_OUT_MODE_P888; ++ else ++ out_mode = vcstate->output_mode; ++ ++ dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode); ++ ++ if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode)) ++ dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP; ++ ++ if (is_yuv_output(vcstate->bus_format)) ++ dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y; ++ ++ vop2_dither_setup(crtc, &dsp_ctrl); ++ ++ vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len); ++ val = hact_st << 16; ++ val |= hact_end; ++ vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val); ++ ++ val = vact_st << 16; ++ val |= vact_end; ++ vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val); ++ ++ if (mode->flags & DRM_MODE_FLAG_INTERLACE) { ++ u16 vact_st_f1 = vtotal + vact_st + 1; ++ u16 vact_end_f1 = vact_st_f1 + vdisplay; ++ ++ val = vact_st_f1 << 16 | vact_end_f1; ++ vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val); ++ ++ val = vtotal << 16 | (vtotal + vsync_len); ++ vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val); ++ dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE; ++ dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL; ++ dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN; ++ vtotal += vtotal + 1; ++ act_end = vact_end_f1; ++ } else { ++ act_end = vact_end; ++ } ++ ++ vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id), ++ (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end); ++ ++ vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len); ++ ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK) { ++ dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV; ++ clock *= 2; ++ } ++ ++ vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0); ++ ++ clk_set_rate(vp->dclk, clock); ++ ++ vop2_post_config(crtc); ++ ++ vop2_cfg_done(vp); ++ ++ vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl); ++ ++ drm_crtc_vblank_on(crtc); ++ ++ vop2_unlock(vop2); ++} ++ ++static int vop2_crtc_atomic_check(struct drm_crtc *crtc, ++ struct drm_atomic_state *state) ++{ ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct drm_plane *plane; ++ int nplanes = 0; ++ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); ++ ++ drm_atomic_crtc_state_for_each_plane(plane, crtc_state) ++ nplanes++; ++ ++ if (nplanes > vp->nlayers) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static bool is_opaque(u16 alpha) ++{ ++ return (alpha >> 8) == 0xff; ++} ++ ++static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config, ++ struct vop2_alpha *alpha) ++{ ++ int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1; ++ int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1; ++ int src_color_mode = alpha_config->src_premulti_en ? ++ ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL; ++ int dst_color_mode = alpha_config->dst_premulti_en ? ++ ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL; ++ ++ alpha->src_color_ctrl.val = 0; ++ alpha->dst_color_ctrl.val = 0; ++ alpha->src_alpha_ctrl.val = 0; ++ alpha->dst_alpha_ctrl.val = 0; ++ ++ if (!alpha_config->src_pixel_alpha_en) ++ alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL; ++ else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en) ++ alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX; ++ else ++ alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL; ++ ++ alpha->src_color_ctrl.bits.alpha_en = 1; ++ ++ if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) { ++ alpha->src_color_ctrl.bits.color_mode = src_color_mode; ++ alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL; ++ } else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) { ++ alpha->src_color_ctrl.bits.color_mode = src_color_mode; ++ alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE; ++ } else { ++ alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL; ++ alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL; ++ } ++ alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8; ++ alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; ++ alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; ++ ++ alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; ++ alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; ++ alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL; ++ alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8; ++ alpha->dst_color_ctrl.bits.color_mode = dst_color_mode; ++ alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE; ++ ++ alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; ++ alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode; ++ alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; ++ alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE; ++ ++ alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; ++ if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en) ++ alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX; ++ else ++ alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL; ++ alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION; ++ alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE; ++} ++ ++static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id) ++{ ++ struct vop2_video_port *vp; ++ int used_layer = 0; ++ int i; ++ ++ for (i = 0; i < port_id; i++) { ++ vp = &vop2->vps[i]; ++ used_layer += hweight32(vp->win_mask); ++ } ++ ++ return used_layer; ++} ++ ++static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win) ++{ ++ u32 offset = (main_win->data->phys_id * 0x10); ++ struct vop2_alpha_config alpha_config; ++ struct vop2_alpha alpha; ++ struct drm_plane_state *bottom_win_pstate; ++ bool src_pixel_alpha_en = false; ++ u16 src_glb_alpha_val, dst_glb_alpha_val; ++ bool premulti_en = false; ++ bool swap = false; ++ ++ /* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */ ++ bottom_win_pstate = main_win->base.state; ++ src_glb_alpha_val = 0; ++ dst_glb_alpha_val = main_win->base.state->alpha; ++ ++ if (!bottom_win_pstate->fb) ++ return; ++ ++ alpha_config.src_premulti_en = premulti_en; ++ alpha_config.dst_premulti_en = false; ++ alpha_config.src_pixel_alpha_en = src_pixel_alpha_en; ++ alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */ ++ alpha_config.src_glb_alpha_value = src_glb_alpha_val; ++ alpha_config.dst_glb_alpha_value = dst_glb_alpha_val; ++ vop2_parse_alpha(&alpha_config, &alpha); ++ ++ alpha.src_color_ctrl.bits.src_dst_swap = swap; ++ vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset, ++ alpha.src_color_ctrl.val); ++ vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset, ++ alpha.dst_color_ctrl.val); ++ vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset, ++ alpha.src_alpha_ctrl.val); ++ vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset, ++ alpha.dst_alpha_ctrl.val); ++} ++ ++static void vop2_setup_alpha(struct vop2_video_port *vp) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ struct drm_framebuffer *fb; ++ struct vop2_alpha_config alpha_config; ++ struct vop2_alpha alpha; ++ struct drm_plane *plane; ++ int pixel_alpha_en; ++ int premulti_en, gpremulti_en = 0; ++ int mixer_id; ++ u32 offset; ++ bool bottom_layer_alpha_en = false; ++ u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE; ++ ++ mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id); ++ alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */ ++ ++ drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { ++ struct vop2_win *win = to_vop2_win(plane); ++ ++ if (plane->state->normalized_zpos == 0 && ++ !is_opaque(plane->state->alpha) && ++ !vop2_cluster_window(win)) { ++ /* ++ * If bottom layer have global alpha effect [except cluster layer, ++ * because cluster have deal with bottom layer global alpha value ++ * at cluster mix], bottom layer mix need deal with global alpha. ++ */ ++ bottom_layer_alpha_en = true; ++ dst_global_alpha = plane->state->alpha; ++ } ++ } ++ ++ drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { ++ struct vop2_win *win = to_vop2_win(plane); ++ int zpos = plane->state->normalized_zpos; ++ ++ if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) ++ premulti_en = 1; ++ else ++ premulti_en = 0; ++ ++ plane = &win->base; ++ fb = plane->state->fb; ++ ++ pixel_alpha_en = fb->format->has_alpha; ++ ++ alpha_config.src_premulti_en = premulti_en; ++ ++ if (bottom_layer_alpha_en && zpos == 1) { ++ gpremulti_en = premulti_en; ++ /* Cd = Cs + (1 - As) * Cd * Agd */ ++ alpha_config.dst_premulti_en = false; ++ alpha_config.src_pixel_alpha_en = pixel_alpha_en; ++ alpha_config.src_glb_alpha_value = plane->state->alpha; ++ alpha_config.dst_glb_alpha_value = dst_global_alpha; ++ } else if (vop2_cluster_window(win)) { ++ /* Mix output data only have pixel alpha */ ++ alpha_config.dst_premulti_en = true; ++ alpha_config.src_pixel_alpha_en = true; ++ alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; ++ alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; ++ } else { ++ /* Cd = Cs + (1 - As) * Cd */ ++ alpha_config.dst_premulti_en = true; ++ alpha_config.src_pixel_alpha_en = pixel_alpha_en; ++ alpha_config.src_glb_alpha_value = plane->state->alpha; ++ alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; ++ } ++ ++ vop2_parse_alpha(&alpha_config, &alpha); ++ ++ offset = (mixer_id + zpos - 1) * 0x10; ++ vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset, ++ alpha.src_color_ctrl.val); ++ vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset, ++ alpha.dst_color_ctrl.val); ++ vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset, ++ alpha.src_alpha_ctrl.val); ++ vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset, ++ alpha.dst_alpha_ctrl.val); ++ } ++ ++ if (vp->id == 0) { ++ if (bottom_layer_alpha_en) { ++ /* Transfer pixel alpha to hdr mix */ ++ alpha_config.src_premulti_en = gpremulti_en; ++ alpha_config.dst_premulti_en = true; ++ alpha_config.src_pixel_alpha_en = true; ++ alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; ++ alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; ++ vop2_parse_alpha(&alpha_config, &alpha); ++ ++ vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, ++ alpha.src_color_ctrl.val); ++ vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL, ++ alpha.dst_color_ctrl.val); ++ vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL, ++ alpha.src_alpha_ctrl.val); ++ vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL, ++ alpha.dst_alpha_ctrl.val); ++ } else { ++ vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0); ++ } ++ } ++} ++ ++static void vop2_setup_layer_mixer(struct vop2_video_port *vp) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ struct drm_plane *plane; ++ u32 layer_sel = 0; ++ u32 port_sel; ++ unsigned int nlayer, ofs; ++ struct drm_display_mode *adjusted_mode; ++ u16 hsync_len; ++ u16 hdisplay; ++ u32 bg_dly; ++ u32 pre_scan_dly; ++ int i; ++ struct vop2_video_port *vp0 = &vop2->vps[0]; ++ struct vop2_video_port *vp1 = &vop2->vps[1]; ++ struct vop2_video_port *vp2 = &vop2->vps[2]; ++ ++ adjusted_mode = &vp->crtc.state->adjusted_mode; ++ hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; ++ hdisplay = adjusted_mode->crtc_hdisplay; ++ ++ bg_dly = vp->data->pre_scan_max_dly[3]; ++ vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id), ++ FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly)); ++ ++ pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len; ++ vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly); ++ ++ vop2_writel(vop2, RK3568_OVL_CTRL, 0); ++ port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL); ++ port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT; ++ ++ if (vp0->nlayers) ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, ++ vp0->nlayers - 1); ++ else ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8); ++ ++ if (vp1->nlayers) ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, ++ (vp0->nlayers + vp1->nlayers - 1)); ++ else ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8); ++ ++ if (vp2->nlayers) ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX, ++ (vp2->nlayers + vp1->nlayers + vp0->nlayers - 1)); ++ else ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8); ++ ++ layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL); ++ ++ ofs = 0; ++ for (i = 0; i < vp->id; i++) ++ ofs += vop2->vps[i].nlayers; ++ ++ nlayer = 0; ++ drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { ++ struct vop2_win *win = to_vop2_win(plane); ++ ++ switch (win->data->phys_id) { ++ case ROCKCHIP_VOP2_CLUSTER0: ++ port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0; ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id); ++ break; ++ case ROCKCHIP_VOP2_CLUSTER1: ++ port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1; ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id); ++ break; ++ case ROCKCHIP_VOP2_ESMART0: ++ port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0; ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id); ++ break; ++ case ROCKCHIP_VOP2_ESMART1: ++ port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1; ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id); ++ break; ++ case ROCKCHIP_VOP2_SMART0: ++ port_sel &= ~RK3568_OVL_PORT_SEL__SMART0; ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id); ++ break; ++ case ROCKCHIP_VOP2_SMART1: ++ port_sel &= ~RK3568_OVL_PORT_SEL__SMART1; ++ port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id); ++ break; ++ } ++ ++ layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7); ++ layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id); ++ nlayer++; ++ } ++ ++ /* configure unused layers to 0x5 (reserved) */ ++ for (; nlayer < 3; nlayer++) { ++ layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7); ++ layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5); ++ } ++ ++ vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel); ++ vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel); ++ vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD); ++} ++ ++static void vop2_setup_dly_for_windows(struct vop2 *vop2) ++{ ++ struct vop2_win *win; ++ int i = 0; ++ u32 cdly = 0, sdly = 0; ++ ++ for (i = 0; i < vop2->data->win_size; i++) { ++ u32 dly; ++ ++ win = &vop2->win[i]; ++ dly = win->delay; ++ ++ switch (win->data->phys_id) { ++ case ROCKCHIP_VOP2_CLUSTER0: ++ cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly); ++ cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly); ++ break; ++ case ROCKCHIP_VOP2_CLUSTER1: ++ cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly); ++ cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly); ++ break; ++ case ROCKCHIP_VOP2_ESMART0: ++ sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly); ++ break; ++ case ROCKCHIP_VOP2_ESMART1: ++ sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly); ++ break; ++ case ROCKCHIP_VOP2_SMART0: ++ sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly); ++ break; ++ case ROCKCHIP_VOP2_SMART1: ++ sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly); ++ break; ++ } ++ } ++ ++ vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly); ++ vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly); ++} ++ ++static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, ++ struct drm_atomic_state *state) ++{ ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct vop2 *vop2 = vp->vop2; ++ struct drm_plane *plane; ++ ++ vp->win_mask = 0; ++ ++ drm_atomic_crtc_for_each_plane(plane, crtc) { ++ struct vop2_win *win = to_vop2_win(plane); ++ ++ win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT]; ++ ++ vp->win_mask |= BIT(win->data->phys_id); ++ ++ if (vop2_cluster_window(win)) ++ vop2_setup_cluster_alpha(vop2, win); ++ } ++ ++ if (!vp->win_mask) ++ return; ++ ++ vop2_setup_layer_mixer(vp); ++ vop2_setup_alpha(vp); ++ vop2_setup_dly_for_windows(vop2); ++} ++ ++static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, ++ struct drm_atomic_state *state) ++{ ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ ++ vop2_post_config(crtc); ++ ++ vop2_cfg_done(vp); ++ ++ spin_lock_irq(&crtc->dev->event_lock); ++ ++ if (crtc->state->event) { ++ WARN_ON(drm_crtc_vblank_get(crtc)); ++ vp->event = crtc->state->event; ++ crtc->state->event = NULL; ++ } ++ ++ spin_unlock_irq(&crtc->dev->event_lock); ++} ++ ++static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = { ++ .mode_fixup = vop2_crtc_mode_fixup, ++ .atomic_check = vop2_crtc_atomic_check, ++ .atomic_begin = vop2_crtc_atomic_begin, ++ .atomic_flush = vop2_crtc_atomic_flush, ++ .atomic_enable = vop2_crtc_atomic_enable, ++ .atomic_disable = vop2_crtc_atomic_disable, ++}; ++ ++static void vop2_crtc_reset(struct drm_crtc *crtc) ++{ ++ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); ++ ++ if (crtc->state) { ++ __drm_atomic_helper_crtc_destroy_state(crtc->state); ++ kfree(vcstate); ++ } ++ ++ vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL); ++ if (!vcstate) ++ return; ++ ++ crtc->state = &vcstate->base; ++ crtc->state->crtc = crtc; ++} ++ ++static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc) ++{ ++ struct rockchip_crtc_state *vcstate, *old_vcstate; ++ ++ old_vcstate = to_rockchip_crtc_state(crtc->state); ++ ++ vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL); ++ if (!vcstate) ++ return NULL; ++ ++ __drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base); ++ ++ return &vcstate->base; ++} ++ ++static void vop2_crtc_destroy_state(struct drm_crtc *crtc, ++ struct drm_crtc_state *state) ++{ ++ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state); ++ ++ __drm_atomic_helper_crtc_destroy_state(&vcstate->base); ++ kfree(vcstate); ++} ++ ++static const struct drm_crtc_funcs vop2_crtc_funcs = { ++ .set_config = drm_atomic_helper_set_config, ++ .page_flip = drm_atomic_helper_page_flip, ++ .destroy = drm_crtc_cleanup, ++ .reset = vop2_crtc_reset, ++ .atomic_duplicate_state = vop2_crtc_duplicate_state, ++ .atomic_destroy_state = vop2_crtc_destroy_state, ++ .enable_vblank = vop2_crtc_enable_vblank, ++ .disable_vblank = vop2_crtc_disable_vblank, ++}; ++ ++static irqreturn_t vop2_isr(int irq, void *data) ++{ ++ struct vop2 *vop2 = data; ++ const struct vop2_data *vop2_data = vop2->data; ++ u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM]; ++ int ret = IRQ_NONE; ++ int i; ++ ++ /* ++ * The irq is shared with the iommu. If the runtime-pm state of the ++ * vop2-device is disabled the irq has to be targeted at the iommu. ++ */ ++ if (!pm_runtime_get_if_in_use(vop2->dev)) ++ return IRQ_NONE; ++ ++ for (i = 0; i < vop2_data->nr_vps; i++) { ++ struct vop2_video_port *vp = &vop2->vps[i]; ++ struct drm_crtc *crtc = &vp->crtc; ++ u32 irqs; ++ ++ irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id)); ++ vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs); ++ ++ if (irqs & VP_INT_DSP_HOLD_VALID) { ++ complete(&vp->dsp_hold_completion); ++ ret = IRQ_HANDLED; ++ } ++ ++ if (irqs & VP_INT_FS_FIELD) { ++ drm_crtc_handle_vblank(crtc); ++ spin_lock(&crtc->dev->event_lock); ++ if (vp->event) { ++ u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE); ++ ++ if (!(val & BIT(vp->id))) { ++ drm_crtc_send_vblank_event(crtc, vp->event); ++ vp->event = NULL; ++ drm_crtc_vblank_put(crtc); ++ } ++ } ++ spin_unlock(&crtc->dev->event_lock); ++ ++ ret = IRQ_HANDLED; ++ } ++ ++ if (irqs & VP_INT_POST_BUF_EMPTY) { ++ drm_err_ratelimited(vop2->drm, ++ "POST_BUF_EMPTY irq err at vp%d\n", ++ vp->id); ++ ret = IRQ_HANDLED; ++ } ++ } ++ ++ axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS); ++ vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]); ++ axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS); ++ vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]); ++ ++ for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) { ++ if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) { ++ drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n"); ++ ret = IRQ_HANDLED; ++ } ++ } ++ ++ pm_runtime_put(vop2->dev); ++ ++ return ret; ++} ++ ++static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win, ++ unsigned long possible_crtcs) ++{ ++ const struct vop2_win_data *win_data = win->data; ++ unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) | ++ BIT(DRM_MODE_BLEND_PREMULTI) | ++ BIT(DRM_MODE_BLEND_COVERAGE); ++ int ret; ++ ++ ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs, ++ &vop2_plane_funcs, win_data->formats, ++ win_data->nformats, ++ win_data->format_modifiers, ++ win->type, win_data->name); ++ if (ret) { ++ drm_err(vop2->drm, "failed to initialize plane %d\n", ret); ++ return ret; ++ } ++ ++ drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs); ++ ++ if (win->data->supported_rotations) ++ drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0, ++ DRM_MODE_ROTATE_0 | ++ win->data->supported_rotations); ++ drm_plane_create_alpha_property(&win->base); ++ drm_plane_create_blend_mode_property(&win->base, blend_caps); ++ drm_plane_create_zpos_property(&win->base, win->win_id, 0, ++ vop2->registered_num_wins - 1); ++ ++ return 0; ++} ++ ++static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n) ++{ ++ int i, id = 0; ++ ++ for (i = 0; i < vop2->data->nr_vps; i++) { ++ struct vop2_video_port *vp = &vop2->vps[i]; ++ ++ if (!vp->crtc.port) ++ continue; ++ ++ if (n == id) ++ return vp; ++ id++; ++ } ++ ++ return NULL; ++} ++ ++#define NR_LAYERS 6 ++ ++static int vop2_create_crtc(struct vop2 *vop2) ++{ ++ const struct vop2_data *vop2_data = vop2->data; ++ struct drm_device *drm = vop2->drm; ++ struct device *dev = vop2->dev; ++ struct drm_plane *plane; ++ struct device_node *port; ++ struct vop2_video_port *vp; ++ u32 possible_crtcs; ++ int i, nvp, nvps = 0; ++ int ret; ++ ++ for (i = 0; i < vop2_data->nr_vps; i++) { ++ const struct vop2_video_port_data *vp_data; ++ struct device_node *np; ++ char dclk_name[9]; ++ ++ vp_data = &vop2_data->vp[i]; ++ vp = &vop2->vps[i]; ++ vp->vop2 = vop2; ++ vp->id = vp_data->id; ++ vp->regs = vp_data->regs; ++ vp->data = vp_data; ++ ++ snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id); ++ vp->dclk = devm_clk_get(vop2->dev, dclk_name); ++ if (IS_ERR(vp->dclk)) { ++ drm_err(vop2->drm, "failed to get %s\n", dclk_name); ++ return PTR_ERR(vp->dclk); ++ } ++ ++ np = of_graph_get_remote_node(dev->of_node, i, -1); ++ if (!np) { ++ drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i); ++ continue; ++ } ++ of_node_put(np); ++ ++ port = of_graph_get_port_by_id(dev->of_node, i); ++ if (!port) { ++ drm_err(vop2->drm, "no port node found for video_port%d\n", i); ++ return -ENOENT; ++ } ++ ++ vp->crtc.port = port; ++ nvps++; ++ } ++ ++ nvp = 0; ++ for (i = 0; i < vop2->registered_num_wins; i++) { ++ struct vop2_win *win = &vop2->win[i]; ++ ++ if (win->type == DRM_PLANE_TYPE_PRIMARY) { ++ vp = get_activated_vp(vop2, nvp); ++ ++ if (vp) { ++ possible_crtcs = BIT(nvp); ++ vp->primary_plane = win; ++ ++ nvp++; ++ } else { ++ /* change the unused primary window to overlay window */ ++ win->type = DRM_PLANE_TYPE_OVERLAY; ++ } ++ } ++ ++ if (win->type == DRM_PLANE_TYPE_OVERLAY) ++ possible_crtcs = (1 << vop2_data->nr_vps) - 1; ++ ++ ret = vop2_plane_init(vop2, win, possible_crtcs); ++ ++ if (ret) { ++ drm_err(vop2->drm, "failed to init plane %s: %d\n", ++ win->data->name, ret); ++ return ret; ++ } ++ } ++ ++ for (i = 0; i < vop2_data->nr_vps; i++) { ++ vp = &vop2->vps[i]; ++ ++ if (!vp->crtc.port) ++ continue; ++ ++ plane = &vp->primary_plane->base; ++ ++ ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL, ++ &vop2_crtc_funcs, ++ "video_port%d", vp->id); ++ if (ret) { ++ drm_err(vop2->drm, "crtc init for video_port%d failed\n", i); ++ return ret; ++ } ++ ++ drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs); ++ ++ init_completion(&vp->dsp_hold_completion); ++ } ++ ++ for (i = 0; i < vop2->data->nr_vps; i++) { ++ struct vop2_video_port *vp = &vop2->vps[i]; ++ ++ if (vp->crtc.port) ++ vp->nlayers = NR_LAYERS / nvps; ++ } ++ ++ return 0; ++} ++ ++static void vop2_destroy_crtc(struct drm_crtc *crtc) ++{ ++ of_node_put(crtc->port); ++ ++ /* ++ * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane() ++ * references the CRTC. ++ */ ++ drm_crtc_cleanup(crtc); ++} ++ ++static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = { ++ [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0), ++ [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5), ++ [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14), ++ [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18), ++ [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31), ++ [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31), ++ [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31), ++ [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31), ++ [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31), ++ [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19), ++ [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15), ++ [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31), ++ [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8), ++ [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9), ++ [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11), ++ ++ /* Scale */ ++ [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15), ++ [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31), ++ [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15), ++ [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13), ++ [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3), ++ [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28), ++ [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29), ++ ++ /* cluster regs */ ++ [VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1), ++ [VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0), ++ [VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7), ++ ++ /* afbc regs */ ++ [VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6), ++ [VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9), ++ [VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10), ++ [VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4), ++ [VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7), ++ [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8), ++ [VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31), ++ [VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31), ++ [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15), ++ [VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31), ++ [VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31), ++ [VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31), ++ [VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31), ++ [VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0), ++ [VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1), ++ [VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2), ++ [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3), ++ [VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff }, ++ [VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff }, ++ [VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff }, ++ [VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff }, ++ [VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff }, ++ [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff }, ++ [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff }, ++ [VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff }, ++ [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff }, ++ [VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff }, ++ [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff }, ++ [VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff }, ++ [VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff }, ++}; ++ ++static int vop2_cluster_init(struct vop2_win *win) ++{ ++ struct vop2 *vop2 = win->vop2; ++ struct reg_field *cluster_regs; ++ int ret, i; ++ ++ cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs), ++ GFP_KERNEL); ++ if (!cluster_regs) ++ return -ENOMEM; ++ ++ for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++) ++ if (cluster_regs[i].reg != 0xffffffff) ++ cluster_regs[i].reg += win->offset; ++ ++ ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg, ++ cluster_regs, ++ ARRAY_SIZE(vop2_cluster_regs)); ++ ++ kfree(cluster_regs); ++ ++ return ret; ++}; ++ ++static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = { ++ [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0), ++ [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5), ++ [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12), ++ [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14), ++ [VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16), ++ [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31), ++ [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31), ++ [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28), ++ [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31), ++ [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31), ++ [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17), ++ [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15), ++ [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31), ++ [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0), ++ [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1), ++ [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3), ++ [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31), ++ [VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29), ++ [VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31), ++ ++ /* Scale */ ++ [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15), ++ [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31), ++ [VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15), ++ [VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31), ++ [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1), ++ [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3), ++ [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5), ++ [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7), ++ [VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9), ++ [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11), ++ [VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13), ++ [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15), ++ [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17), ++ [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8), ++ [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9), ++ [VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10), ++ [VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11), ++ [VOP2_WIN_XMIRROR] = { .reg = 0xffffffff }, ++ [VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff }, ++ [VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff }, ++ [VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff }, ++}; ++ ++static int vop2_esmart_init(struct vop2_win *win) ++{ ++ struct vop2 *vop2 = win->vop2; ++ struct reg_field *esmart_regs; ++ int ret, i; ++ ++ esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs), ++ GFP_KERNEL); ++ if (!esmart_regs) ++ return -ENOMEM; ++ ++ for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++) ++ if (esmart_regs[i].reg != 0xffffffff) ++ esmart_regs[i].reg += win->offset; ++ ++ ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg, ++ esmart_regs, ++ ARRAY_SIZE(vop2_esmart_regs)); ++ ++ kfree(esmart_regs); ++ ++ return ret; ++}; ++ ++static int vop2_win_init(struct vop2 *vop2) ++{ ++ const struct vop2_data *vop2_data = vop2->data; ++ struct vop2_win *win; ++ int i, ret; ++ ++ for (i = 0; i < vop2_data->win_size; i++) { ++ const struct vop2_win_data *win_data = &vop2_data->win[i]; ++ ++ win = &vop2->win[i]; ++ win->data = win_data; ++ win->type = win_data->type; ++ win->offset = win_data->base; ++ win->win_id = i; ++ win->vop2 = vop2; ++ if (vop2_cluster_window(win)) ++ ret = vop2_cluster_init(win); ++ else ++ ret = vop2_esmart_init(win); ++ if (ret) ++ return ret; ++ } ++ ++ vop2->registered_num_wins = vop2_data->win_size; ++ ++ return 0; ++} ++ ++/* ++ * The window registers are only updated when config done is written. ++ * Until that they read back the old value. As we read-modify-write ++ * these registers mark them as non-volatile. This makes sure we read ++ * the new values from the regmap register cache. ++ */ ++static const struct regmap_range vop2_nonvolatile_range[] = { ++ regmap_reg_range(0x1000, 0x23ff), ++}; ++ ++static const struct regmap_access_table vop2_volatile_table = { ++ .no_ranges = vop2_nonvolatile_range, ++ .n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range), ++}; ++ ++static const struct regmap_config vop2_regmap_config = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x3000, ++ .name = "vop2", ++ .volatile_table = &vop2_volatile_table, ++ .cache_type = REGCACHE_RBTREE, ++}; ++ ++static int vop2_bind(struct device *dev, struct device *master, void *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ const struct vop2_data *vop2_data; ++ struct drm_device *drm = data; ++ struct vop2 *vop2; ++ struct resource *res; ++ size_t alloc_size; ++ int ret; ++ ++ vop2_data = of_device_get_match_data(dev); ++ if (!vop2_data) ++ return -ENODEV; ++ ++ /* Allocate vop2 struct and its vop2_win array */ ++ alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size; ++ vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL); ++ if (!vop2) ++ return -ENOMEM; ++ ++ vop2->dev = dev; ++ vop2->data = vop2_data; ++ vop2->drm = drm; ++ ++ dev_set_drvdata(dev, vop2); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); ++ if (!res) { ++ drm_err(vop2->drm, "failed to get vop2 register byname\n"); ++ return -EINVAL; ++ } ++ ++ vop2->regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(vop2->regs)) ++ return PTR_ERR(vop2->regs); ++ vop2->len = resource_size(res); ++ ++ vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config); ++ ++ ret = vop2_win_init(vop2); ++ if (ret) ++ return ret; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut"); ++ if (res) { ++ vop2->lut_regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(vop2->lut_regs)) ++ return PTR_ERR(vop2->lut_regs); ++ } ++ ++ vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); ++ ++ vop2->hclk = devm_clk_get(vop2->dev, "hclk"); ++ if (IS_ERR(vop2->hclk)) { ++ drm_err(vop2->drm, "failed to get hclk source\n"); ++ return PTR_ERR(vop2->hclk); ++ } ++ ++ vop2->aclk = devm_clk_get(vop2->dev, "aclk"); ++ if (IS_ERR(vop2->aclk)) { ++ drm_err(vop2->drm, "failed to get aclk source\n"); ++ return PTR_ERR(vop2->aclk); ++ } ++ ++ vop2->irq = platform_get_irq(pdev, 0); ++ if (vop2->irq < 0) { ++ drm_err(vop2->drm, "cannot find irq for vop2\n"); ++ return vop2->irq; ++ } ++ ++ mutex_init(&vop2->vop2_lock); ++ ++ ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2); ++ if (ret) ++ return ret; ++ ++ ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev); ++ if (ret) { ++ drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret); ++ return ret; ++ } ++ ++ ret = vop2_create_crtc(vop2); ++ if (ret) ++ return ret; ++ ++ pm_runtime_enable(&pdev->dev); ++ ++ return 0; ++} ++ ++static void vop2_unbind(struct device *dev, struct device *master, void *data) ++{ ++ struct vop2 *vop2 = dev_get_drvdata(dev); ++ struct drm_device *drm = vop2->drm; ++ struct list_head *plane_list = &drm->mode_config.plane_list; ++ struct list_head *crtc_list = &drm->mode_config.crtc_list; ++ struct drm_crtc *crtc, *tmpc; ++ struct drm_plane *plane, *tmpp; ++ ++ rockchip_drm_dma_detach_device(vop2->drm, vop2->dev); ++ ++ pm_runtime_disable(dev); ++ ++ list_for_each_entry_safe(plane, tmpp, plane_list, head) ++ drm_plane_cleanup(plane); ++ ++ list_for_each_entry_safe(crtc, tmpc, crtc_list, head) ++ vop2_destroy_crtc(crtc); ++} ++ ++const struct component_ops vop2_component_ops = { ++ .bind = vop2_bind, ++ .unbind = vop2_unbind, ++}; ++EXPORT_SYMBOL_GPL(vop2_component_ops); +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +new file mode 100644 +index 000000000000..c727093a06d6 +--- /dev/null ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +@@ -0,0 +1,477 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd ++ * Author:Mark Yao ++ */ ++ ++#ifndef _ROCKCHIP_DRM_VOP2_H ++#define _ROCKCHIP_DRM_VOP2_H ++ ++#include "rockchip_drm_vop.h" ++ ++#include ++#include ++ ++#define VOP_FEATURE_OUTPUT_10BIT BIT(0) ++ ++#define WIN_FEATURE_AFBDC BIT(0) ++#define WIN_FEATURE_CLUSTER BIT(1) ++ ++/* ++ * the delay number of a window in different mode. ++ */ ++enum win_dly_mode { ++ VOP2_DLY_MODE_DEFAULT, /**< default mode */ ++ VOP2_DLY_MODE_HISO_S, /** HDR in SDR out mode, as a SDR window */ ++ VOP2_DLY_MODE_HIHO_H, /** HDR in HDR out mode, as a HDR window */ ++ VOP2_DLY_MODE_MAX, ++}; ++ ++struct vop_rect { ++ int width; ++ int height; ++}; ++ ++enum vop2_scale_up_mode { ++ VOP2_SCALE_UP_NRST_NBOR, ++ VOP2_SCALE_UP_BIL, ++ VOP2_SCALE_UP_BIC, ++}; ++ ++enum vop2_scale_down_mode { ++ VOP2_SCALE_DOWN_NRST_NBOR, ++ VOP2_SCALE_DOWN_BIL, ++ VOP2_SCALE_DOWN_AVG, ++}; ++ ++enum vop2_win_regs { ++ VOP2_WIN_ENABLE, ++ VOP2_WIN_FORMAT, ++ VOP2_WIN_CSC_MODE, ++ VOP2_WIN_XMIRROR, ++ VOP2_WIN_YMIRROR, ++ VOP2_WIN_RB_SWAP, ++ VOP2_WIN_UV_SWAP, ++ VOP2_WIN_ACT_INFO, ++ VOP2_WIN_DSP_INFO, ++ VOP2_WIN_DSP_ST, ++ VOP2_WIN_YRGB_MST, ++ VOP2_WIN_UV_MST, ++ VOP2_WIN_YRGB_VIR, ++ VOP2_WIN_UV_VIR, ++ VOP2_WIN_YUV_CLIP, ++ VOP2_WIN_Y2R_EN, ++ VOP2_WIN_R2Y_EN, ++ VOP2_WIN_COLOR_KEY, ++ VOP2_WIN_COLOR_KEY_EN, ++ VOP2_WIN_DITHER_UP, ++ ++ /* scale regs */ ++ VOP2_WIN_SCALE_YRGB_X, ++ VOP2_WIN_SCALE_YRGB_Y, ++ VOP2_WIN_SCALE_CBCR_X, ++ VOP2_WIN_SCALE_CBCR_Y, ++ VOP2_WIN_YRGB_HOR_SCL_MODE, ++ VOP2_WIN_YRGB_HSCL_FILTER_MODE, ++ VOP2_WIN_YRGB_VER_SCL_MODE, ++ VOP2_WIN_YRGB_VSCL_FILTER_MODE, ++ VOP2_WIN_CBCR_VER_SCL_MODE, ++ VOP2_WIN_CBCR_HSCL_FILTER_MODE, ++ VOP2_WIN_CBCR_HOR_SCL_MODE, ++ VOP2_WIN_CBCR_VSCL_FILTER_MODE, ++ VOP2_WIN_VSD_CBCR_GT2, ++ VOP2_WIN_VSD_CBCR_GT4, ++ VOP2_WIN_VSD_YRGB_GT2, ++ VOP2_WIN_VSD_YRGB_GT4, ++ VOP2_WIN_BIC_COE_SEL, ++ ++ /* cluster regs */ ++ VOP2_WIN_CLUSTER_ENABLE, ++ VOP2_WIN_AFBC_ENABLE, ++ VOP2_WIN_CLUSTER_LB_MODE, ++ ++ /* afbc regs */ ++ VOP2_WIN_AFBC_FORMAT, ++ VOP2_WIN_AFBC_RB_SWAP, ++ VOP2_WIN_AFBC_UV_SWAP, ++ VOP2_WIN_AFBC_AUTO_GATING_EN, ++ VOP2_WIN_AFBC_BLOCK_SPLIT_EN, ++ VOP2_WIN_AFBC_PIC_VIR_WIDTH, ++ VOP2_WIN_AFBC_TILE_NUM, ++ VOP2_WIN_AFBC_PIC_OFFSET, ++ VOP2_WIN_AFBC_PIC_SIZE, ++ VOP2_WIN_AFBC_DSP_OFFSET, ++ VOP2_WIN_AFBC_TRANSFORM_OFFSET, ++ VOP2_WIN_AFBC_HDR_PTR, ++ VOP2_WIN_AFBC_HALF_BLOCK_EN, ++ VOP2_WIN_AFBC_ROTATE_270, ++ VOP2_WIN_AFBC_ROTATE_90, ++ VOP2_WIN_MAX_REG, ++}; ++ ++struct vop2_win_data { ++ const char *name; ++ unsigned int phys_id; ++ ++ u32 base; ++ enum drm_plane_type type; ++ ++ u32 nformats; ++ const u32 *formats; ++ const uint64_t *format_modifiers; ++ const unsigned int supported_rotations; ++ ++ /** ++ * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2 ++ */ ++ unsigned int layer_sel_id; ++ uint64_t feature; ++ ++ unsigned int max_upscale_factor; ++ unsigned int max_downscale_factor; ++ const u8 dly[VOP2_DLY_MODE_MAX]; ++}; ++ ++struct vop2_video_port_data { ++ unsigned int id; ++ u32 feature; ++ u16 gamma_lut_len; ++ u16 cubic_lut_len; ++ struct vop_rect max_output; ++ const u8 pre_scan_max_dly[4]; ++ const struct vop2_video_port_regs *regs; ++ unsigned int offset; ++}; ++ ++struct vop2_data { ++ u8 nr_vps; ++ const struct vop2_ctrl *ctrl; ++ const struct vop2_win_data *win; ++ const struct vop2_video_port_data *vp; ++ const struct vop_csc_table *csc_table; ++ struct vop_rect max_input; ++ struct vop_rect max_output; ++ ++ unsigned int win_size; ++ unsigned int soc_id; ++}; ++ ++/* interrupt define */ ++#define FS_NEW_INTR BIT(4) ++#define ADDR_SAME_INTR BIT(5) ++#define LINE_FLAG1_INTR BIT(6) ++#define WIN0_EMPTY_INTR BIT(7) ++#define WIN1_EMPTY_INTR BIT(8) ++#define WIN2_EMPTY_INTR BIT(9) ++#define WIN3_EMPTY_INTR BIT(10) ++#define HWC_EMPTY_INTR BIT(11) ++#define POST_BUF_EMPTY_INTR BIT(12) ++#define PWM_GEN_INTR BIT(13) ++#define DMA_FINISH_INTR BIT(14) ++#define FS_FIELD_INTR BIT(15) ++#define FE_INTR BIT(16) ++#define WB_UV_FIFO_FULL_INTR BIT(17) ++#define WB_YRGB_FIFO_FULL_INTR BIT(18) ++#define WB_COMPLETE_INTR BIT(19) ++ ++/* ++ * display output interface supported by rockchip lcdc ++ */ ++#define ROCKCHIP_OUT_MODE_P888 0 ++#define ROCKCHIP_OUT_MODE_BT1120 0 ++#define ROCKCHIP_OUT_MODE_P666 1 ++#define ROCKCHIP_OUT_MODE_P565 2 ++#define ROCKCHIP_OUT_MODE_BT656 5 ++#define ROCKCHIP_OUT_MODE_S888 8 ++#define ROCKCHIP_OUT_MODE_S888_DUMMY 12 ++#define ROCKCHIP_OUT_MODE_YUV420 14 ++/* for use special outface */ ++#define ROCKCHIP_OUT_MODE_AAAA 15 ++ ++enum vop_csc_format { ++ CSC_BT601L, ++ CSC_BT709L, ++ CSC_BT601F, ++ CSC_BT2020, ++}; ++ ++enum src_factor_mode { ++ SRC_FAC_ALPHA_ZERO, ++ SRC_FAC_ALPHA_ONE, ++ SRC_FAC_ALPHA_DST, ++ SRC_FAC_ALPHA_DST_INVERSE, ++ SRC_FAC_ALPHA_SRC, ++ SRC_FAC_ALPHA_SRC_GLOBAL, ++}; ++ ++enum dst_factor_mode { ++ DST_FAC_ALPHA_ZERO, ++ DST_FAC_ALPHA_ONE, ++ DST_FAC_ALPHA_SRC, ++ DST_FAC_ALPHA_SRC_INVERSE, ++ DST_FAC_ALPHA_DST, ++ DST_FAC_ALPHA_DST_GLOBAL, ++}; ++ ++#define RK3568_GRF_VO_CON1 0x0364 ++/* System registers definition */ ++#define RK3568_REG_CFG_DONE 0x000 ++#define RK3568_VERSION_INFO 0x004 ++#define RK3568_SYS_AUTO_GATING_CTRL 0x008 ++#define RK3568_SYS_AXI_LUT_CTRL 0x024 ++#define RK3568_DSP_IF_EN 0x028 ++#define RK3568_DSP_IF_CTRL 0x02c ++#define RK3568_DSP_IF_POL 0x030 ++#define RK3568_WB_CTRL 0x40 ++#define RK3568_WB_XSCAL_FACTOR 0x44 ++#define RK3568_WB_YRGB_MST 0x48 ++#define RK3568_WB_CBR_MST 0x4C ++#define RK3568_OTP_WIN_EN 0x050 ++#define RK3568_LUT_PORT_SEL 0x058 ++#define RK3568_SYS_STATUS0 0x060 ++#define RK3568_VP_LINE_FLAG(vp) (0x70 + (vp) * 0x4) ++#define RK3568_SYS0_INT_EN 0x80 ++#define RK3568_SYS0_INT_CLR 0x84 ++#define RK3568_SYS0_INT_STATUS 0x88 ++#define RK3568_SYS1_INT_EN 0x90 ++#define RK3568_SYS1_INT_CLR 0x94 ++#define RK3568_SYS1_INT_STATUS 0x98 ++#define RK3568_VP_INT_EN(vp) (0xA0 + (vp) * 0x10) ++#define RK3568_VP_INT_CLR(vp) (0xA4 + (vp) * 0x10) ++#define RK3568_VP_INT_STATUS(vp) (0xA8 + (vp) * 0x10) ++#define RK3568_VP_INT_RAW_STATUS(vp) (0xAC + (vp) * 0x10) ++ ++/* Video Port registers definition */ ++#define RK3568_VP_DSP_CTRL 0x00 ++#define RK3568_VP_MIPI_CTRL 0x04 ++#define RK3568_VP_COLOR_BAR_CTRL 0x08 ++#define RK3568_VP_3D_LUT_CTRL 0x10 ++#define RK3568_VP_3D_LUT_MST 0x20 ++#define RK3568_VP_DSP_BG 0x2C ++#define RK3568_VP_PRE_SCAN_HTIMING 0x30 ++#define RK3568_VP_POST_DSP_HACT_INFO 0x34 ++#define RK3568_VP_POST_DSP_VACT_INFO 0x38 ++#define RK3568_VP_POST_SCL_FACTOR_YRGB 0x3C ++#define RK3568_VP_POST_SCL_CTRL 0x40 ++#define RK3568_VP_POST_DSP_VACT_INFO_F1 0x44 ++#define RK3568_VP_DSP_HTOTAL_HS_END 0x48 ++#define RK3568_VP_DSP_HACT_ST_END 0x4C ++#define RK3568_VP_DSP_VTOTAL_VS_END 0x50 ++#define RK3568_VP_DSP_VACT_ST_END 0x54 ++#define RK3568_VP_DSP_VS_ST_END_F1 0x58 ++#define RK3568_VP_DSP_VACT_ST_END_F1 0x5C ++#define RK3568_VP_BCSH_CTRL 0x60 ++#define RK3568_VP_BCSH_BCS 0x64 ++#define RK3568_VP_BCSH_H 0x68 ++#define RK3568_VP_BCSH_COLOR_BAR 0x6C ++ ++/* Overlay registers definition */ ++#define RK3568_OVL_CTRL 0x600 ++#define RK3568_OVL_LAYER_SEL 0x604 ++#define RK3568_OVL_PORT_SEL 0x608 ++#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL 0x610 ++#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL 0x614 ++#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL 0x618 ++#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL 0x61C ++#define RK3568_MIX0_SRC_COLOR_CTRL 0x650 ++#define RK3568_MIX0_DST_COLOR_CTRL 0x654 ++#define RK3568_MIX0_SRC_ALPHA_CTRL 0x658 ++#define RK3568_MIX0_DST_ALPHA_CTRL 0x65C ++#define RK3568_HDR0_SRC_COLOR_CTRL 0x6C0 ++#define RK3568_HDR0_DST_COLOR_CTRL 0x6C4 ++#define RK3568_HDR0_SRC_ALPHA_CTRL 0x6C8 ++#define RK3568_HDR0_DST_ALPHA_CTRL 0x6CC ++#define RK3568_VP_BG_MIX_CTRL(vp) (0x6E0 + (vp) * 4) ++#define RK3568_CLUSTER_DLY_NUM 0x6F0 ++#define RK3568_SMART_DLY_NUM 0x6F8 ++ ++/* Cluster register definition, offset relative to window base */ ++#define RK3568_CLUSTER_WIN_CTRL0 0x00 ++#define RK3568_CLUSTER_WIN_CTRL1 0x04 ++#define RK3568_CLUSTER_WIN_YRGB_MST 0x10 ++#define RK3568_CLUSTER_WIN_CBR_MST 0x14 ++#define RK3568_CLUSTER_WIN_VIR 0x18 ++#define RK3568_CLUSTER_WIN_ACT_INFO 0x20 ++#define RK3568_CLUSTER_WIN_DSP_INFO 0x24 ++#define RK3568_CLUSTER_WIN_DSP_ST 0x28 ++#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB 0x30 ++#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET 0x3C ++#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL 0x50 ++#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE 0x54 ++#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR 0x58 ++#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH 0x5C ++#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE 0x60 ++#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET 0x64 ++#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET 0x68 ++#define RK3568_CLUSTER_WIN_AFBCD_CTRL 0x6C ++ ++#define RK3568_CLUSTER_CTRL 0x100 ++ ++/* (E)smart register definition, offset relative to window base */ ++#define RK3568_SMART_CTRL0 0x00 ++#define RK3568_SMART_CTRL1 0x04 ++#define RK3568_SMART_REGION0_CTRL 0x10 ++#define RK3568_SMART_REGION0_YRGB_MST 0x14 ++#define RK3568_SMART_REGION0_CBR_MST 0x18 ++#define RK3568_SMART_REGION0_VIR 0x1C ++#define RK3568_SMART_REGION0_ACT_INFO 0x20 ++#define RK3568_SMART_REGION0_DSP_INFO 0x24 ++#define RK3568_SMART_REGION0_DSP_ST 0x28 ++#define RK3568_SMART_REGION0_SCL_CTRL 0x30 ++#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB 0x34 ++#define RK3568_SMART_REGION0_SCL_FACTOR_CBR 0x38 ++#define RK3568_SMART_REGION0_SCL_OFFSET 0x3C ++#define RK3568_SMART_REGION1_CTRL 0x40 ++#define RK3568_SMART_REGION1_YRGB_MST 0x44 ++#define RK3568_SMART_REGION1_CBR_MST 0x48 ++#define RK3568_SMART_REGION1_VIR 0x4C ++#define RK3568_SMART_REGION1_ACT_INFO 0x50 ++#define RK3568_SMART_REGION1_DSP_INFO 0x54 ++#define RK3568_SMART_REGION1_DSP_ST 0x58 ++#define RK3568_SMART_REGION1_SCL_CTRL 0x60 ++#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB 0x64 ++#define RK3568_SMART_REGION1_SCL_FACTOR_CBR 0x68 ++#define RK3568_SMART_REGION1_SCL_OFFSET 0x6C ++#define RK3568_SMART_REGION2_CTRL 0x70 ++#define RK3568_SMART_REGION2_YRGB_MST 0x74 ++#define RK3568_SMART_REGION2_CBR_MST 0x78 ++#define RK3568_SMART_REGION2_VIR 0x7C ++#define RK3568_SMART_REGION2_ACT_INFO 0x80 ++#define RK3568_SMART_REGION2_DSP_INFO 0x84 ++#define RK3568_SMART_REGION2_DSP_ST 0x88 ++#define RK3568_SMART_REGION2_SCL_CTRL 0x90 ++#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB 0x94 ++#define RK3568_SMART_REGION2_SCL_FACTOR_CBR 0x98 ++#define RK3568_SMART_REGION2_SCL_OFFSET 0x9C ++#define RK3568_SMART_REGION3_CTRL 0xA0 ++#define RK3568_SMART_REGION3_YRGB_MST 0xA4 ++#define RK3568_SMART_REGION3_CBR_MST 0xA8 ++#define RK3568_SMART_REGION3_VIR 0xAC ++#define RK3568_SMART_REGION3_ACT_INFO 0xB0 ++#define RK3568_SMART_REGION3_DSP_INFO 0xB4 ++#define RK3568_SMART_REGION3_DSP_ST 0xB8 ++#define RK3568_SMART_REGION3_SCL_CTRL 0xC0 ++#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB 0xC4 ++#define RK3568_SMART_REGION3_SCL_FACTOR_CBR 0xC8 ++#define RK3568_SMART_REGION3_SCL_OFFSET 0xCC ++#define RK3568_SMART_COLOR_KEY_CTRL 0xD0 ++ ++/* HDR register definition */ ++#define RK3568_HDR_LUT_CTRL 0x2000 ++#define RK3568_HDR_LUT_MST 0x2004 ++#define RK3568_SDR2HDR_CTRL 0x2010 ++#define RK3568_HDR2SDR_CTRL 0x2020 ++#define RK3568_HDR2SDR_SRC_RANGE 0x2024 ++#define RK3568_HDR2SDR_NORMFACEETF 0x2028 ++#define RK3568_HDR2SDR_DST_RANGE 0x202C ++#define RK3568_HDR2SDR_NORMFACCGAMMA 0x2030 ++#define RK3568_HDR_EETF_OETF_Y0 0x203C ++#define RK3568_HDR_SAT_Y0 0x20C0 ++#define RK3568_HDR_EOTF_OETF_Y0 0x20F0 ++#define RK3568_HDR_OETF_DX_POW1 0x2200 ++#define RK3568_HDR_OETF_XN1 0x2300 ++ ++#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN BIT(15) ++ ++#define RK3568_VP_DSP_CTRL__STANDBY BIT(31) ++#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE BIT(20) ++#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL GENMASK(19, 18) ++#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN BIT(17) ++#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN BIT(16) ++#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y BIT(15) ++#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP BIT(9) ++#define RK3568_VP_DSP_CTRL__DSP_INTERLACE BIT(7) ++#define RK3568_VP_DSP_CTRL__DSP_FILED_POL BIT(6) ++#define RK3568_VP_DSP_CTRL__P2I_EN BIT(5) ++#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV BIT(4) ++#define RK3568_VP_DSP_CTRL__OUT_MODE GENMASK(3, 0) ++ ++#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN BIT(1) ++#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN BIT(0) ++ ++#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX GENMASK(26, 25) ++#define RK3568_SYS_DSP_INFACE_EN_LVDS1 BIT(24) ++#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX GENMASK(22, 21) ++#define RK3568_SYS_DSP_INFACE_EN_MIPI1 BIT(20) ++#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX GENMASK(19, 18) ++#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX GENMASK(17, 16) ++#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX GENMASK(15, 14) ++#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX GENMASK(11, 10) ++#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX GENMASK(9, 8) ++#define RK3568_SYS_DSP_INFACE_EN_LVDS0 BIT(5) ++#define RK3568_SYS_DSP_INFACE_EN_MIPI0 BIT(4) ++#define RK3568_SYS_DSP_INFACE_EN_EDP BIT(3) ++#define RK3568_SYS_DSP_INFACE_EN_HDMI BIT(1) ++#define RK3568_SYS_DSP_INFACE_EN_RGB BIT(0) ++ ++#define RK3568_DSP_IF_POL__MIPI_PIN_POL GENMASK(19, 16) ++#define RK3568_DSP_IF_POL__EDP_PIN_POL GENMASK(15, 12) ++#define RK3568_DSP_IF_POL__HDMI_PIN_POL GENMASK(7, 4) ++#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL GENMASK(3, 0) ++ ++#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK BIT(5) ++#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2 BIT(4) ++ ++#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN BIT(31) ++ ++#define RK3568_DSP_IF_POL__CFG_DONE_IMD BIT(28) ++ ++#define VOP2_SYS_AXI_BUS_NUM 2 ++ ++#define VOP2_CLUSTER_YUV444_10 0x12 ++ ++#define VOP2_COLOR_KEY_MASK BIT(31) ++ ++#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD BIT(28) ++ ++#define RK3568_VP_BG_MIX_CTRL__BG_DLY GENMASK(31, 24) ++ ++#define RK3568_OVL_PORT_SEL__SEL_PORT GENMASK(31, 16) ++#define RK3568_OVL_PORT_SEL__SMART1 GENMASK(31, 30) ++#define RK3568_OVL_PORT_SEL__SMART0 GENMASK(29, 28) ++#define RK3568_OVL_PORT_SEL__ESMART1 GENMASK(27, 26) ++#define RK3568_OVL_PORT_SEL__ESMART0 GENMASK(25, 24) ++#define RK3568_OVL_PORT_SEL__CLUSTER1 GENMASK(19, 18) ++#define RK3568_OVL_PORT_SEL__CLUSTER0 GENMASK(17, 16) ++#define RK3568_OVL_PORT_SET__PORT2_MUX GENMASK(11, 8) ++#define RK3568_OVL_PORT_SET__PORT1_MUX GENMASK(7, 4) ++#define RK3568_OVL_PORT_SET__PORT0_MUX GENMASK(3, 0) ++#define RK3568_OVL_LAYER_SEL__LAYER(layer, x) ((x) << ((layer) * 4)) ++ ++#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1 GENMASK(31, 24) ++#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0 GENMASK(23, 16) ++#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1 GENMASK(15, 8) ++#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0 GENMASK(7, 0) ++ ++#define RK3568_SMART_DLY_NUM__SMART1 GENMASK(31, 24) ++#define RK3568_SMART_DLY_NUM__SMART0 GENMASK(23, 16) ++#define RK3568_SMART_DLY_NUM__ESMART1 GENMASK(15, 8) ++#define RK3568_SMART_DLY_NUM__ESMART0 GENMASK(7, 0) ++ ++#define VP_INT_DSP_HOLD_VALID BIT(6) ++#define VP_INT_FS_FIELD BIT(5) ++#define VP_INT_POST_BUF_EMPTY BIT(4) ++#define VP_INT_LINE_FLAG1 BIT(3) ++#define VP_INT_LINE_FLAG0 BIT(2) ++#define VOP2_INT_BUS_ERRPR BIT(1) ++#define VP_INT_FS BIT(0) ++ ++#define POLFLAG_DCLK_INV BIT(3) ++ ++enum vop2_layer_phy_id { ++ ROCKCHIP_VOP2_CLUSTER0 = 0, ++ ROCKCHIP_VOP2_CLUSTER1, ++ ROCKCHIP_VOP2_ESMART0, ++ ROCKCHIP_VOP2_ESMART1, ++ ROCKCHIP_VOP2_SMART0, ++ ROCKCHIP_VOP2_SMART1, ++ ROCKCHIP_VOP2_CLUSTER2, ++ ROCKCHIP_VOP2_CLUSTER3, ++ ROCKCHIP_VOP2_ESMART2, ++ ROCKCHIP_VOP2_ESMART3, ++ ROCKCHIP_VOP2_PHY_ID_INVALID = -1, ++}; ++ ++extern const struct component_ops vop2_component_ops; ++ ++#endif /* _ROCKCHIP_DRM_VOP2_H */ +diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c +index be74c87a8be4..4ced073c6b06 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c ++++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c +@@ -36,12 +36,6 @@ + + struct rockchip_lvds; + +-#define connector_to_lvds(c) \ +- container_of(c, struct rockchip_lvds, connector) +- +-#define encoder_to_lvds(c) \ +- container_of(c, struct rockchip_lvds, encoder) +- + /** + * struct rockchip_lvds_soc_data - rockchip lvds Soc private data + * @probe: LVDS platform probe function +@@ -65,10 +59,22 @@ struct rockchip_lvds { + struct drm_panel *panel; + struct drm_bridge *bridge; + struct drm_connector connector; +- struct drm_encoder encoder; ++ struct rockchip_encoder encoder; + struct dev_pin_info *pins; + }; + ++static inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *connector) ++{ ++ return container_of(connector, struct rockchip_lvds, connector); ++} ++ ++static inline struct rockchip_lvds *encoder_to_lvds(struct drm_encoder *encoder) ++{ ++ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); ++ ++ return container_of(rkencoder, struct rockchip_lvds, encoder); ++} ++ + static inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset, + u32 val) + { +@@ -599,7 +605,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, + goto err_put_remote; + } + +- encoder = &lvds->encoder; ++ encoder = &lvds->encoder.encoder; + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, + dev->of_node); + +@@ -674,10 +680,10 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master, + const struct drm_encoder_helper_funcs *encoder_funcs; + + encoder_funcs = lvds->soc_data->helper_funcs; +- encoder_funcs->disable(&lvds->encoder); ++ encoder_funcs->disable(&lvds->encoder.encoder); + pm_runtime_disable(dev); + drm_connector_cleanup(&lvds->connector); +- drm_encoder_cleanup(&lvds->encoder); ++ drm_encoder_cleanup(&lvds->encoder.encoder); + } + + static const struct component_ops rockchip_lvds_component_ops = { +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +new file mode 100644 +index 000000000000..9bf0637bf8e2 +--- /dev/null ++++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +@@ -0,0 +1,281 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) Rockchip Electronics Co.Ltd ++ * Author: Andy Yan ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rockchip_drm_vop2.h" ++ ++static const uint32_t formats_win_full_10bit[] = { ++ 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, ++}; ++ ++static const uint32_t formats_win_full_10bit_yuyv[] = { ++ 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_YVYU, ++ DRM_FORMAT_VYUY, ++}; ++ ++static const uint32_t formats_win_lite[] = { ++ DRM_FORMAT_XRGB8888, ++ DRM_FORMAT_ARGB8888, ++ DRM_FORMAT_XBGR8888, ++ DRM_FORMAT_ABGR8888, ++ DRM_FORMAT_RGB888, ++ DRM_FORMAT_BGR888, ++ DRM_FORMAT_RGB565, ++ DRM_FORMAT_BGR565, ++}; ++ ++static const uint64_t format_modifiers[] = { ++ DRM_FORMAT_MOD_LINEAR, ++ DRM_FORMAT_MOD_INVALID, ++}; ++ ++static const uint64_t format_modifiers_afbc[] = { ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16), ++ ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_SPARSE), ++ ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_YTR), ++ ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_CBR), ++ ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_YTR | ++ AFBC_FORMAT_MOD_SPARSE), ++ ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_CBR | ++ AFBC_FORMAT_MOD_SPARSE), ++ ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_YTR | ++ AFBC_FORMAT_MOD_CBR), ++ ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_YTR | ++ AFBC_FORMAT_MOD_CBR | ++ AFBC_FORMAT_MOD_SPARSE), ++ ++ /* SPLIT mandates SPARSE, RGB modes mandates YTR */ ++ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | ++ AFBC_FORMAT_MOD_YTR | ++ AFBC_FORMAT_MOD_SPARSE | ++ AFBC_FORMAT_MOD_SPLIT), ++ DRM_FORMAT_MOD_INVALID, ++}; ++ ++static const struct vop2_video_port_data rk3568_vop_video_ports[] = { ++ { ++ .id = 0, ++ .feature = VOP_FEATURE_OUTPUT_10BIT, ++ .gamma_lut_len = 1024, ++ .cubic_lut_len = 9 * 9 * 9, ++ .max_output = { 4096, 2304 }, ++ .pre_scan_max_dly = { 69, 53, 53, 42 }, ++ .offset = 0xc00, ++ }, { ++ .id = 1, ++ .gamma_lut_len = 1024, ++ .max_output = { 2048, 1536 }, ++ .pre_scan_max_dly = { 40, 40, 40, 40 }, ++ .offset = 0xd00, ++ }, { ++ .id = 2, ++ .gamma_lut_len = 1024, ++ .max_output = { 1920, 1080 }, ++ .pre_scan_max_dly = { 40, 40, 40, 40 }, ++ .offset = 0xe00, ++ }, ++}; ++ ++/* ++ * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win. ++ * Every cluster can work as 4K win or split into two win. ++ * All win in cluster support AFBCD. ++ * ++ * Every esmart win and smart win support 4 Multi-region. ++ * ++ * Scale filter mode: ++ * ++ * * Cluster: bicubic for horizontal scale up, others use bilinear ++ * * ESmart: ++ * * nearest-neighbor/bilinear/bicubic for scale up ++ * * nearest-neighbor/bilinear/average for scale down ++ * ++ * ++ * @TODO describe the wind like cpu-map dt nodes; ++ */ ++static const struct vop2_win_data rk3568_vop_win_data[] = { ++ { ++ .name = "Smart0-win0", ++ .phys_id = ROCKCHIP_VOP2_SMART0, ++ .base = 0x1c00, ++ .formats = formats_win_lite, ++ .nformats = ARRAY_SIZE(formats_win_lite), ++ .format_modifiers = format_modifiers, ++ .layer_sel_id = 3, ++ .supported_rotations = DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_PRIMARY, ++ .max_upscale_factor = 8, ++ .max_downscale_factor = 8, ++ .dly = { 20, 47, 41 }, ++ }, { ++ .name = "Smart1-win0", ++ .phys_id = ROCKCHIP_VOP2_SMART1, ++ .formats = formats_win_lite, ++ .nformats = ARRAY_SIZE(formats_win_lite), ++ .format_modifiers = format_modifiers, ++ .base = 0x1e00, ++ .layer_sel_id = 7, ++ .supported_rotations = DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_PRIMARY, ++ .max_upscale_factor = 8, ++ .max_downscale_factor = 8, ++ .dly = { 20, 47, 41 }, ++ }, { ++ .name = "Esmart1-win0", ++ .phys_id = ROCKCHIP_VOP2_ESMART1, ++ .formats = formats_win_full_10bit_yuyv, ++ .nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv), ++ .format_modifiers = format_modifiers, ++ .base = 0x1a00, ++ .layer_sel_id = 6, ++ .supported_rotations = DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_PRIMARY, ++ .max_upscale_factor = 8, ++ .max_downscale_factor = 8, ++ .dly = { 20, 47, 41 }, ++ }, { ++ .name = "Esmart0-win0", ++ .phys_id = ROCKCHIP_VOP2_ESMART0, ++ .formats = formats_win_full_10bit_yuyv, ++ .nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv), ++ .format_modifiers = format_modifiers, ++ .base = 0x1800, ++ .layer_sel_id = 2, ++ .supported_rotations = DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ .max_upscale_factor = 8, ++ .max_downscale_factor = 8, ++ .dly = { 20, 47, 41 }, ++ }, { ++ .name = "Cluster0-win0", ++ .phys_id = ROCKCHIP_VOP2_CLUSTER0, ++ .base = 0x1000, ++ .formats = formats_win_full_10bit, ++ .nformats = ARRAY_SIZE(formats_win_full_10bit), ++ .format_modifiers = format_modifiers_afbc, ++ .layer_sel_id = 0, ++ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | ++ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, ++ .max_upscale_factor = 4, ++ .max_downscale_factor = 4, ++ .dly = { 0, 27, 21 }, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER, ++ }, { ++ .name = "Cluster1-win0", ++ .phys_id = ROCKCHIP_VOP2_CLUSTER1, ++ .base = 0x1200, ++ .formats = formats_win_full_10bit, ++ .nformats = ARRAY_SIZE(formats_win_full_10bit), ++ .format_modifiers = format_modifiers_afbc, ++ .layer_sel_id = 1, ++ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | ++ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ .max_upscale_factor = 4, ++ .max_downscale_factor = 4, ++ .dly = { 0, 27, 21 }, ++ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER, ++ }, ++}; ++ ++static const struct vop2_data rk3566_vop = { ++ .nr_vps = 3, ++ .max_input = { 4096, 2304 }, ++ .max_output = { 4096, 2304 }, ++ .vp = rk3568_vop_video_ports, ++ .win = rk3568_vop_win_data, ++ .win_size = ARRAY_SIZE(rk3568_vop_win_data), ++ .soc_id = 3566, ++}; ++ ++static const struct vop2_data rk3568_vop = { ++ .nr_vps = 3, ++ .max_input = { 4096, 2304 }, ++ .max_output = { 4096, 2304 }, ++ .vp = rk3568_vop_video_ports, ++ .win = rk3568_vop_win_data, ++ .win_size = ARRAY_SIZE(rk3568_vop_win_data), ++ .soc_id = 3568, ++}; ++ ++static const struct of_device_id vop2_dt_match[] = { ++ { ++ .compatible = "rockchip,rk3566-vop", ++ .data = &rk3566_vop, ++ }, { ++ .compatible = "rockchip,rk3568-vop", ++ .data = &rk3568_vop, ++ }, { ++ }, ++}; ++MODULE_DEVICE_TABLE(of, vop2_dt_match); ++ ++static int vop2_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ ++ return component_add(dev, &vop2_component_ops); ++} ++ ++static int vop2_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &vop2_component_ops); ++ ++ return 0; ++} ++ ++struct platform_driver vop2_platform_driver = { ++ .probe = vop2_probe, ++ .remove = vop2_remove, ++ .driver = { ++ .name = "rockchip-vop2", ++ .of_match_table = of_match_ptr(vop2_dt_match), ++ }, ++}; +diff --git a/include/dt-bindings/soc/rockchip,vop2.h b/include/dt-bindings/soc/rockchip,vop2.h +new file mode 100644 +index 000000000000..0a87bc90564a +--- /dev/null ++++ b/include/dt-bindings/soc/rockchip,vop2.h +@@ -0,0 +1,14 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ ++ ++#ifndef __DT_BINDINGS_ROCKCHIP_VOP2_H ++#define __DT_BINDINGS_ROCKCHIP_VOP2_H ++ ++#define RK3568_VOP2_EP_RGB 0 ++#define RK3568_VOP2_EP_HDMI 1 ++#define RK3568_VOP2_EP_EDP 2 ++#define RK3568_VOP2_EP_MIPI0 3 ++#define RK3568_VOP2_EP_LVDS0 4 ++#define RK3568_VOP2_EP_MIPI1 5 ++#define RK3568_VOP2_EP_LVDS1 6 ++ ++#endif /* __DT_BINDINGS_ROCKCHIP_VOP2_H */ +-- +2.30.2 + diff --git a/patch/kernel/archive/rk35xx-5.17/rk356x-gpu-GPU.patch b/patch/kernel/archive/rk35xx-5.17/rk356x-gpu-GPU.patch new file mode 100644 index 0000000000..e6536a05ec --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk356x-gpu-GPU.patch @@ -0,0 +1,180 @@ +diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml +index 63a08f3f321d..4d6bfae0653c 100644 +--- a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml ++++ b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml +@@ -159,6 +159,21 @@ allOf: + power-domains: + maxItems: 1 + sram-supply: false ++ - if: ++ properties: ++ compatible: ++ contains: ++ const: rockchip,rk3568-mali ++ then: ++ properties: ++ clocks: ++ minItems: 2 ++ clock-names: ++ items: ++ - const: gpu ++ - const: bus ++ required: ++ - clock-names + + examples: + - | + +diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +index ff1689283996..50bbea862a6a 100644 +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -144,6 +144,40 @@ scmi_clk: protocol@14 { + }; + }; + ++ gpu_opp_table: opp-table-1 { ++ compatible = "operating-points-v2"; ++ ++ opp-200000000 { ++ opp-hz = /bits/ 64 <200000000>; ++ opp-microvolt = <825000>; ++ }; ++ ++ opp-300000000 { ++ opp-hz = /bits/ 64 <300000000>; ++ opp-microvolt = <825000>; ++ }; ++ ++ opp-400000000 { ++ opp-hz = /bits/ 64 <400000000>; ++ opp-microvolt = <825000>; ++ }; ++ ++ opp-600000000 { ++ opp-hz = /bits/ 64 <600000000>; ++ opp-microvolt = <825000>; ++ }; ++ ++ opp-700000000 { ++ opp-hz = /bits/ 64 <700000000>; ++ opp-microvolt = <900000>; ++ }; ++ ++ opp-800000000 { ++ opp-hz = /bits/ 64 <800000000>; ++ opp-microvolt = <1000000>; ++ }; ++ }; ++ + pmu { + compatible = "arm,cortex-a55-pmu"; + interrupts = , +@@ -444,6 +478,21 @@ power-domain@RK3568_PD_RKVENC { + }; + }; + ++ gpu: gpu@fde60000 { ++ compatible = "rockchip,rk3568-mali", "arm,mali-bifrost"; ++ reg = <0x0 0xfde60000 0x0 0x4000>; ++ interrupts = , ++ , ++ ; ++ interrupt-names = "job", "mmu", "gpu"; ++ clocks = <&scmi_clk 1>, <&cru CLK_GPU>; ++ clock-names = "gpu", "bus"; ++ #cooling-cells = <2>; ++ operating-points-v2 = <&gpu_opp_table>; ++ power-domains = <&power RK3568_PD_GPU>; ++ status = "disabled"; ++ }; ++ + sdmmc2: mmc@fe000000 { + compatible = "rockchip,rk3568-dw-mshc", "rockchip,rk3288-dw-mshc"; + reg = <0x0 0xfe000000 0x0 0x4000>; + +diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +index 50bbea862a6a..37194d735028 100644 +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -1093,6 +1093,33 @@ gpu_thermal: gpu-thermal { + polling-delay = <1000>; /* milliseconds */ + + thermal-sensors = <&tsadc 1>; ++ ++ trips { ++ gpu_threshold: gpu-threshold { ++ temperature = <70000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ gpu_target: gpu-target { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ gpu_crit: gpu-crit { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ map0 { ++ trip = <&gpu_target>; ++ cooling-device = ++ <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; ++ }; ++ }; ++ + }; + }; + + +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +index 3e65465ac7d5..b048db6cff3a 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +@@ -221,6 +221,11 @@ &gmac1m0_clkinout + status = "okay"; + }; + ++&gpu { ++ mali-supply = <&vdd_gpu>; ++ status = "okay"; ++}; ++ + &i2c0 { + status = "okay"; + + +diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts +index d8a4f7a9f562..39c495ff0157 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts +@@ -140,6 +140,11 @@ &gmac1m1_rgmii_clk + status = "okay"; + }; + ++&gpu { ++ mali-supply = <&vdd_gpu>; ++ status = "okay"; ++}; ++ + &i2c0 { + status = "okay"; + +@@ -462,6 +467,12 @@ &sdmmc0 { + status = "okay"; + }; + ++&tsadc { ++ rockchip,hw-tshut-mode = <1>; ++ rockchip,hw-tshut-polarity = <0>; ++ status = "okay"; ++}; ++ + &uart2 { + status = "okay"; + }; diff --git a/patch/kernel/archive/rk35xx-5.17/rk356x-hdmi-audio.patch b/patch/kernel/archive/rk35xx-5.17/rk356x-hdmi-audio.patch new file mode 100644 index 0000000000..1aec35d1cc --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk356x-hdmi-audio.patch @@ -0,0 +1,103 @@ +This adds the i2s0 node and an hdmi-sound sound device to the +rk356x device tree. On the rk356[68], the i2s0 controller is +connected to HDMI audio. + +Tested-by: Michael Riesch +Signed-off-by: Nicolas Frattaroli +--- + +Changes in v2: + - reordered nodes to conform + - reordered properties to conform + - add Michael Riesch's Tested-by + + arch/arm64/boot/dts/rockchip/rk356x.dtsi | 33 ++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +index 3c09cf6d4c37..aafb622dfa83 100644 +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -174,6 +174,22 @@ scmi_clk: protocol@14 { + }; + }; + ++ hdmi_sound: hdmi-sound { ++ compatible = "simple-audio-card"; ++ simple-audio-card,name = "HDMI"; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,mclk-fs = <256>; ++ status = "disabled"; ++ ++ simple-audio-card,codec { ++ sound-dai = <&hdmi>; ++ }; ++ ++ simple-audio-card,cpu { ++ sound-dai = <&i2s0_8ch>; ++ }; ++ }; ++ + pmu { + compatible = "arm,cortex-a55-pmu"; + interrupts = , +@@ -789,6 +805,23 @@ spdif: spdif@fe460000 { + status = "disabled"; + }; + ++ i2s0_8ch: i2s@fe400000 { ++ compatible = "rockchip,rk3568-i2s-tdm"; ++ reg = <0x0 0xfe400000 0x0 0x1000>; ++ interrupts = ; ++ assigned-clocks = <&cru CLK_I2S0_8CH_TX_SRC>, <&cru CLK_I2S0_8CH_RX_SRC>; ++ assigned-clock-rates = <1188000000>, <1188000000>; ++ clocks = <&cru MCLK_I2S0_8CH_TX>, <&cru MCLK_I2S0_8CH_RX>, <&cru HCLK_I2S0_8CH>; ++ clock-names = "mclk_tx", "mclk_rx", "hclk"; ++ dmas = <&dmac1 0>; ++ dma-names = "tx"; ++ resets = <&cru SRST_M_I2S0_8CH_TX>, <&cru SRST_M_I2S0_8CH_RX>; ++ reset-names = "tx-m", "rx-m"; ++ rockchip,grf = <&grf>; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ + i2s1_8ch: i2s@fe410000 { + compatible = "rockchip,rk3568-i2s-tdm"; + reg = <0x0 0xfe410000 0x0 0x1000>; + + +This enables the i2s0 controller and the hdmi-sound node on +the PINE64 Quartz64 Model A single-board computer. + +Signed-off-by: Nicolas Frattaroli +--- + arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +index a4453c82b03d..0598510dce58 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +@@ -215,6 +215,10 @@ &hdmi_in_vp0 { + status = "okay"; + }; + ++&hdmi_sound { ++ status = "okay"; ++}; ++ + &gpu { + mali-supply = <&vdd_gpu>; + status = "okay"; +@@ -444,6 +448,10 @@ regulator-state-mem { + }; + }; + ++&i2s0_8ch { ++ status = "okay"; ++}; ++ + &i2s1_8ch { + pinctrl-names = "default"; + pinctrl-0 = <&i2s1m0_sclktx diff --git a/patch/kernel/archive/rk35xx-5.17/rk356x-mfd-rk808-add-reboot-support-to-rk808.c.patch b/patch/kernel/archive/rk35xx-5.17/rk356x-mfd-rk808-add-reboot-support-to-rk808.c.patch new file mode 100644 index 0000000000..24f4dbe32f --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk356x-mfd-rk808-add-reboot-support-to-rk808.c.patch @@ -0,0 +1,95 @@ +diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c +index b181fe401330..874d461dda44 100644 +--- a/drivers/mfd/rk808.c ++++ b/drivers/mfd/rk808.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + struct rk808_reg_data { + int addr; +@@ -543,6 +544,7 @@ static void rk808_pm_power_off(void) + reg = RK808_DEVCTRL_REG, + bit = DEV_OFF_RST; + break; ++ case RK809_ID: + case RK817_ID: + reg = RK817_SYS_CFG(3); + bit = DEV_OFF; +@@ -559,6 +561,34 @@ static void rk808_pm_power_off(void) + dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n"); + } + ++static int rk808_restart_notify(struct notifier_block *this, unsigned long mode, void *cmd) ++{ ++ struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); ++ unsigned int reg, bit; ++ int ret; ++ ++ switch (rk808->variant) { ++ case RK809_ID: ++ case RK817_ID: ++ reg = RK817_SYS_CFG(3); ++ bit = DEV_RST; ++ break; ++ ++ default: ++ return NOTIFY_DONE; ++ } ++ ret = regmap_update_bits(rk808->regmap, reg, bit, bit); ++ if (ret) ++ dev_err(&rk808_i2c_client->dev, "Failed to restart device!\n"); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block rk808_restart_handler = { ++ .notifier_call = rk808_restart_notify, ++ .priority = 192, ++}; ++ + static void rk8xx_shutdown(struct i2c_client *client) + { + struct rk808 *rk808 = i2c_get_clientdata(client); +@@ -727,6 +757,18 @@ static int rk808_probe(struct i2c_client *client, + if (of_property_read_bool(np, "rockchip,system-power-controller")) { + rk808_i2c_client = client; + pm_power_off = rk808_pm_power_off; ++ ++ switch (rk808->variant) { ++ case RK809_ID: ++ case RK817_ID: ++ ret = register_restart_handler(&rk808_restart_handler); ++ if (ret) ++ dev_warn(&client->dev, "failed to register restart handler, %d\n", ret); ++ break; ++ default: ++ dev_dbg(&client->dev, "pmic controlled board reset not supported\n"); ++ break; ++ } + } + + return 0; +@@ -749,6 +791,8 @@ static int rk808_remove(struct i2c_client *client) + if (pm_power_off == rk808_pm_power_off) + pm_power_off = NULL; + ++ unregister_restart_handler(&rk808_restart_handler); ++ + return 0; + } + +diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h +index a96e6d43ca06..58602032e642 100644 +--- a/include/linux/mfd/rk808.h ++++ b/include/linux/mfd/rk808.h +@@ -373,6 +373,7 @@ enum rk805_reg { + #define SWITCH2_EN BIT(6) + #define SWITCH1_EN BIT(5) + #define DEV_OFF_RST BIT(3) ++#define DEV_RST BIT(2) + #define DEV_OFF BIT(0) + #define RTC_STOP BIT(0) + diff --git a/patch/kernel/archive/rk35xx-5.17/rk356x-naneng-combo-phy.patch b/patch/kernel/archive/rk35xx-5.17/rk356x-naneng-combo-phy.patch new file mode 100644 index 0000000000..404ab5a9c3 --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk356x-naneng-combo-phy.patch @@ -0,0 +1,987 @@ +From: Johan Jonker + +Add naneng combo phy register compatible. + +Acked-by: Rob Herring +Signed-off-by: Johan Jonker +Signed-off-by: Yifeng Zhao +--- + +Changes in v7: None +Changes in v5: None +Changes in v4: None +Changes in v3: None +Changes in v2: None + + Documentation/devicetree/bindings/mfd/syscon.yaml | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml +index fdd96e378df0..e9bb96ab9446 100644 +--- a/Documentation/devicetree/bindings/mfd/syscon.yaml ++++ b/Documentation/devicetree/bindings/mfd/syscon.yaml +@@ -52,6 +52,8 @@ properties: + - rockchip,rk3288-qos + - rockchip,rk3368-qos + - rockchip,rk3399-qos ++ - rockchip,rk3568-pipe-grf ++ - rockchip,rk3568-pipe-phy-grf + - rockchip,rk3568-qos + - samsung,exynos3-sysreg + - samsung,exynos4-sysreg + +Add the compatible strings for the Naneng combo PHY found on rockchip SoC. + +Reviewed-by: Rob Herring +Signed-off-by: Yifeng Zhao +Signed-off-by: Johan Jonker +--- + +Changes in v7: +- remove u3otg0_port_en, u3otg1_port_en and pipe_sgmii_mac_sel + +Changes in v5: +- modify description for ssc and ext-refclk +- remove apb reset + +Changes in v4: +- restyle +- remove some minItems +- add more properties +- remove reset-names +- move #phy-cells +- add rockchip,rk3568-pipe-grf +- add rockchip,rk3568-pipe-phy-grf + +Changes in v3: None +Changes in v2: +- Fix dtschema/dtc warnings/errors + + .../phy/phy-rockchip-naneng-combphy.yaml | 109 ++++++++++++++++++ + 1 file changed, 109 insertions(+) + create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml + +diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml +new file mode 100644 +index 000000000000..f14454401419 +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml +@@ -0,0 +1,109 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/phy/phy-rockchip-naneng-combphy.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Rockchip SoC Naneng Combo Phy Device Tree Bindings ++ ++maintainers: ++ - Heiko Stuebner ++ ++properties: ++ compatible: ++ enum: ++ - rockchip,rk3568-naneng-combphy ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: reference clock ++ - description: apb clock ++ - description: pipe clock ++ ++ clock-names: ++ items: ++ - const: ref ++ - const: apb ++ - const: pipe ++ ++ resets: ++ items: ++ - description: exclusive PHY reset line ++ ++ rockchip,enable-ssc: ++ type: boolean ++ description: ++ The option SSC can be enabled for U3, SATA and PCIE. ++ Most commercially available platforms use SSC to reduce EMI. ++ ++ rockchip,ext-refclk: ++ type: boolean ++ description: ++ Many PCIe connections, especially backplane connections, ++ require a synchronous reference clock between the two link partners. ++ To achieve this a common clock source, referred to as REFCLK in ++ the PCI Express Card Electromechanical Specification, ++ should be used by both ends of the PCIe link. ++ In PCIe mode one can choose to use an internal or an external reference ++ clock. ++ By default the internal clock is selected. The PCIe PHY provides a 100MHz ++ differential clock output(optional with SSC) for system applications. ++ When selecting this option an externally 100MHz differential ++ reference clock needs to be provided to the PCIe PHY. ++ ++ rockchip,pipe-grf: ++ $ref: /schemas/types.yaml#/definitions/phandle ++ description: ++ Some additional phy settings are accessed through GRF regs. ++ ++ rockchip,pipe-phy-grf: ++ $ref: /schemas/types.yaml#/definitions/phandle ++ description: ++ Some additional pipe settings are accessed through GRF regs. ++ ++ "#phy-cells": ++ const: 1 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-names ++ - resets ++ - rockchip,pipe-grf ++ - rockchip,pipe-phy-grf ++ - "#phy-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ ++ pipegrf: syscon@fdc50000 { ++ compatible = "rockchip,rk3568-pipe-grf", "syscon"; ++ reg = <0xfdc50000 0x1000>; ++ }; ++ ++ pipe_phy_grf0: syscon@fdc70000 { ++ compatible = "rockchip,rk3568-pipe-phy-grf", "syscon"; ++ reg = <0xfdc70000 0x1000>; ++ }; ++ ++ combphy0: phy@fe820000 { ++ compatible = "rockchip,rk3568-naneng-combphy"; ++ reg = <0xfe820000 0x100>; ++ clocks = <&pmucru CLK_PCIEPHY0_REF>, ++ <&cru PCLK_PIPEPHY0>, ++ <&cru PCLK_PIPE>; ++ clock-names = "ref", "apb", "pipe"; ++ assigned-clocks = <&pmucru CLK_PCIEPHY0_REF>; ++ assigned-clock-rates = <100000000>; ++ resets = <&cru SRST_PIPEPHY0>; ++ rockchip,pipe-grf = <&pipegrf>; ++ rockchip,pipe-phy-grf = <&pipe_phy_grf0>; ++ #phy-cells = <1>; ++ }; + +This patch implements a combo phy driver for Rockchip SoCs +with NaNeng IP block. This phy can be used as pcie-phy, usb3-phy, +sata-phy or sgmii-phy. + +Signed-off-by: Yifeng Zhao +Signed-off-by: Johan Jonker +--- + +Changes in v7: +- rename regs +- remove pipe_sgmii_mac_sel, u3otg0_port_en and u3otg1_port_en + +Changes in v5: +- add rockchip_combphy_updatel() +- restyle + +Changes in v4: +- restyle +- add devm_reset_control_array_get() +- remove clk structure +- change refclk DT parse +- change dev_err message +- add dot to phrase +- add ext_refclk variable +- add enable_ssc variable +- rename rockchip_combphy_param_write +- remove param_read +- replace rockchip-naneng-combphy driver name + +Changes in v3: +- Using api devm_reset_control_get_optional_exclusive and dev_err_probe. +- Remove apb_rst. +- Redefine registers address. + +Changes in v2: +- Using api devm_platform_get_and_ioremap_resource. +- Modify rockchip_combphy_set_Mode. +- Add some PHY registers definition. + + drivers/phy/rockchip/Kconfig | 8 + + drivers/phy/rockchip/Makefile | 1 + + .../rockchip/phy-rockchip-naneng-combphy.c | 589 ++++++++++++++++++ + 3 files changed, 598 insertions(+) + create mode 100644 drivers/phy/rockchip/phy-rockchip-naneng-combphy.c + +diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig +index e812adad7242..9022e395c056 100644 +--- a/drivers/phy/rockchip/Kconfig ++++ b/drivers/phy/rockchip/Kconfig +@@ -66,6 +66,14 @@ config PHY_ROCKCHIP_INNO_DSIDPHY + Enable this to support the Rockchip MIPI/LVDS/TTL PHY with + Innosilicon IP block. + ++config PHY_ROCKCHIP_NANENG_COMBO_PHY ++ tristate "Rockchip NANENG COMBO PHY Driver" ++ depends on ARCH_ROCKCHIP && OF ++ select GENERIC_PHY ++ help ++ Enable this to support the Rockchip PCIe/USB3.0/SATA/QSGMII ++ combo PHY with NaNeng IP block. ++ + config PHY_ROCKCHIP_PCIE + tristate "Rockchip PCIe PHY Driver" + depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST +diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile +index f0eec212b2aa..a5041efb5b8f 100644 +--- a/drivers/phy/rockchip/Makefile ++++ b/drivers/phy/rockchip/Makefile +@@ -6,6 +6,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o + obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o + obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o + obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o ++obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o + obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o + obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o + obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o +diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c +new file mode 100644 +index 000000000000..47137a5c448a +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c +@@ -0,0 +1,589 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Rockchip PIPE USB3.0 PCIE SATA Combo Phy driver ++ * ++ * Copyright (C) 2021 Rockchip Electronics Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BIT_WRITEABLE_SHIFT 16 ++#define REF_CLOCK_24MHz (24 * HZ_PER_MHZ) ++#define REF_CLOCK_25MHz (25 * HZ_PER_MHZ) ++#define REF_CLOCK_100MHz (100 * HZ_PER_MHZ) ++ ++/* COMBO PHY REG */ ++#define PHYREG6 0x14 ++#define PHYREG6_PLL_DIV_MASK GENMASK(7, 6) ++#define PHYREG6_PLL_DIV_SHIFT 6 ++#define PHYREG6_PLL_DIV_2 1 ++ ++#define PHYREG7 0x18 ++#define PHYREG7_TX_RTERM_MASK GENMASK(7, 4) ++#define PHYREG7_TX_RTERM_SHIFT 4 ++#define PHYREG7_TX_RTERM_50OHM 8 ++#define PHYREG7_RX_RTERM_MASK GENMASK(3, 0) ++#define PHYREG7_RX_RTERM_SHIFT 0 ++#define PHYREG7_RX_RTERM_44OHM 15 ++ ++#define PHYREG8 0x1C ++#define PHYREG8_SSC_EN BIT(4) ++ ++#define PHYREG11 0x28 ++#define PHYREG11_SU_TRIM_0_7 0xF0 ++ ++#define PHYREG12 0x2C ++#define PHYREG12_PLL_LPF_ADJ_VALUE 4 ++ ++#define PHYREG13 0x30 ++#define PHYREG13_RESISTER_MASK GENMASK(5, 4) ++#define PHYREG13_RESISTER_SHIFT 0x4 ++#define PHYREG13_RESISTER_HIGH_Z 3 ++#define PHYREG13_CKRCV_AMP0 BIT(7) ++ ++#define PHYREG14 0x34 ++#define PHYREG14_CKRCV_AMP1 BIT(0) ++ ++#define PHYREG15 0x38 ++#define PHYREG15_CTLE_EN BIT(0) ++#define PHYREG15_SSC_CNT_MASK GENMASK(7, 6) ++#define PHYREG15_SSC_CNT_SHIFT 6 ++#define PHYREG15_SSC_CNT_VALUE 1 ++ ++#define PHYREG16 0x3C ++#define PHYREG16_SSC_CNT_VALUE 0x5f ++ ++#define PHYREG18 0x44 ++#define PHYREG18_PLL_LOOP 0x32 ++ ++#define PHYREG32 0x7C ++#define PHYREG32_SSC_MASK GENMASK(7, 4) ++#define PHYREG32_SSC_DIR_SHIFT 4 ++#define PHYREG32_SSC_UPWARD 0 ++#define PHYREG32_SSC_DOWNWARD 1 ++#define PHYREG32_SSC_OFFSET_SHIFT 6 ++#define PHYREG32_SSC_OFFSET_500PPM 1 ++ ++#define PHYREG33 0x80 ++#define PHYREG33_PLL_KVCO_MASK GENMASK(4, 2) ++#define PHYREG33_PLL_KVCO_SHIFT 2 ++#define PHYREG33_PLL_KVCO_VALUE 2 ++ ++struct rockchip_combphy_priv; ++ ++struct combphy_reg { ++ u16 offset; ++ u16 bitend; ++ u16 bitstart; ++ u16 disable; ++ u16 enable; ++}; ++ ++struct rockchip_combphy_grfcfg { ++ struct combphy_reg pcie_mode_set; ++ struct combphy_reg usb_mode_set; ++ struct combphy_reg sgmii_mode_set; ++ struct combphy_reg qsgmii_mode_set; ++ struct combphy_reg pipe_rxterm_set; ++ struct combphy_reg pipe_txelec_set; ++ struct combphy_reg pipe_txcomp_set; ++ struct combphy_reg pipe_clk_25m; ++ struct combphy_reg pipe_clk_100m; ++ struct combphy_reg pipe_phymode_sel; ++ struct combphy_reg pipe_rate_sel; ++ struct combphy_reg pipe_rxterm_sel; ++ struct combphy_reg pipe_txelec_sel; ++ struct combphy_reg pipe_txcomp_sel; ++ struct combphy_reg pipe_clk_ext; ++ struct combphy_reg pipe_sel_usb; ++ struct combphy_reg pipe_sel_qsgmii; ++ struct combphy_reg pipe_phy_status; ++ struct combphy_reg con0_for_pcie; ++ struct combphy_reg con1_for_pcie; ++ struct combphy_reg con2_for_pcie; ++ struct combphy_reg con3_for_pcie; ++ struct combphy_reg con0_for_sata; ++ struct combphy_reg con1_for_sata; ++ struct combphy_reg con2_for_sata; ++ struct combphy_reg con3_for_sata; ++ struct combphy_reg pipe_con0_for_sata; ++ struct combphy_reg pipe_xpcs_phy_ready; ++}; ++ ++struct rockchip_combphy_cfg { ++ const struct rockchip_combphy_grfcfg *grfcfg; ++ int (*combphy_cfg)(struct rockchip_combphy_priv *priv); ++}; ++ ++struct rockchip_combphy_priv { ++ u8 mode; ++ void __iomem *mmio; ++ int num_clks; ++ struct clk_bulk_data *clks; ++ struct device *dev; ++ struct regmap *pipe_grf; ++ struct regmap *phy_grf; ++ struct phy *phy; ++ struct reset_control *phy_rst; ++ const struct rockchip_combphy_cfg *cfg; ++ bool enable_ssc; ++ bool ext_refclk; ++ struct clk *refclk; ++}; ++ ++static void rockchip_combphy_updatel(struct rockchip_combphy_priv *priv, ++ int mask, int val, int reg) ++{ ++ unsigned int temp; ++ ++ temp = readl(priv->mmio + reg); ++ temp = (temp & ~(mask)) | val; ++ writel(temp, priv->mmio + reg); ++} ++ ++static int rockchip_combphy_param_write(struct regmap *base, ++ const struct combphy_reg *reg, bool en) ++{ ++ u32 val, mask, tmp; ++ ++ tmp = en ? reg->enable : reg->disable; ++ mask = GENMASK(reg->bitend, reg->bitstart); ++ val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); ++ ++ return regmap_write(base, reg->offset, val); ++} ++ ++static u32 rockchip_combphy_is_ready(struct rockchip_combphy_priv *priv) ++{ ++ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; ++ u32 mask, val; ++ ++ mask = GENMASK(cfg->pipe_phy_status.bitend, ++ cfg->pipe_phy_status.bitstart); ++ ++ regmap_read(priv->phy_grf, cfg->pipe_phy_status.offset, &val); ++ val = (val & mask) >> cfg->pipe_phy_status.bitstart; ++ ++ return val; ++} ++ ++static int rockchip_combphy_set_mode(struct rockchip_combphy_priv *priv) ++{ ++ int ret = 0; ++ ++ switch (priv->mode) { ++ case PHY_TYPE_PCIE: ++ case PHY_TYPE_USB3: ++ case PHY_TYPE_SATA: ++ case PHY_TYPE_SGMII: ++ case PHY_TYPE_QSGMII: ++ if (priv->cfg->combphy_cfg) ++ ret = priv->cfg->combphy_cfg(priv); ++ break; ++ default: ++ dev_err(priv->dev, "incompatible PHY type\n"); ++ return -EINVAL; ++ } ++ ++ if (ret) ++ dev_err(priv->dev, "failed to init phy for phy mode %x\n", priv->mode); ++ ++ return ret; ++} ++ ++static int rockchip_combphy_init(struct phy *phy) ++{ ++ struct rockchip_combphy_priv *priv = phy_get_drvdata(phy); ++ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; ++ u32 val; ++ int ret; ++ ++ ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); ++ if (ret) { ++ dev_err(priv->dev, "failed to enable clks\n"); ++ return ret; ++ } ++ ++ ret = rockchip_combphy_set_mode(priv); ++ if (ret) ++ goto err_clk; ++ ++ ret = reset_control_deassert(priv->phy_rst); ++ if (ret) ++ goto err_clk; ++ ++ if (priv->mode == PHY_TYPE_USB3) { ++ ret = readx_poll_timeout_atomic(rockchip_combphy_is_ready, ++ priv, val, ++ val == cfg->pipe_phy_status.enable, ++ 10, 1000); ++ if (ret) ++ dev_warn(priv->dev, "wait phy status ready timeout\n"); ++ } ++ ++ return 0; ++ ++err_clk: ++ clk_bulk_disable_unprepare(priv->num_clks, priv->clks); ++ ++ return ret; ++} ++ ++static int rockchip_combphy_exit(struct phy *phy) ++{ ++ struct rockchip_combphy_priv *priv = phy_get_drvdata(phy); ++ ++ clk_bulk_disable_unprepare(priv->num_clks, priv->clks); ++ reset_control_assert(priv->phy_rst); ++ ++ return 0; ++} ++ ++static const struct phy_ops rochchip_combphy_ops = { ++ .init = rockchip_combphy_init, ++ .exit = rockchip_combphy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static struct phy *rockchip_combphy_xlate(struct device *dev, struct of_phandle_args *args) ++{ ++ struct rockchip_combphy_priv *priv = dev_get_drvdata(dev); ++ ++ if (args->args_count != 1) { ++ dev_err(dev, "invalid number of arguments\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ if (priv->mode != PHY_NONE && priv->mode != args->args[0]) ++ dev_warn(dev, "phy type select %d overwriting type %d\n", ++ args->args[0], priv->mode); ++ ++ priv->mode = args->args[0]; ++ ++ return priv->phy; ++} ++ ++static int rockchip_combphy_parse_dt(struct device *dev, struct rockchip_combphy_priv *priv) ++{ ++ int i; ++ ++ priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks); ++ if (priv->num_clks < 1) ++ return -EINVAL; ++ ++ priv->refclk = NULL; ++ for (i = 0; i < priv->num_clks; i++) { ++ if (!strncmp(priv->clks[i].id, "ref", 3)) { ++ priv->refclk = priv->clks[i].clk; ++ break; ++ } ++ } ++ ++ if (!priv->refclk) { ++ dev_err(dev, "no refclk found\n"); ++ return -EINVAL; ++ } ++ ++ priv->pipe_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-grf"); ++ if (IS_ERR(priv->pipe_grf)) { ++ dev_err(dev, "failed to find peri_ctrl pipe-grf regmap\n"); ++ return PTR_ERR(priv->pipe_grf); ++ } ++ ++ priv->phy_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-phy-grf"); ++ if (IS_ERR(priv->phy_grf)) { ++ dev_err(dev, "failed to find peri_ctrl pipe-phy-grf regmap\n"); ++ return PTR_ERR(priv->phy_grf); ++ } ++ ++ priv->enable_ssc = device_property_present(dev, "rockchip,enable-ssc"); ++ ++ priv->ext_refclk = device_property_present(dev, "rockchip,ext-refclk"); ++ ++ priv->phy_rst = devm_reset_control_array_get(dev, false, false); ++ if (IS_ERR(priv->phy_rst)) ++ return dev_err_probe(dev, PTR_ERR(priv->phy_rst), "failed to get phy reset\n"); ++ ++ return 0; ++} ++ ++static int rockchip_combphy_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct device *dev = &pdev->dev; ++ struct rockchip_combphy_priv *priv; ++ const struct rockchip_combphy_cfg *phy_cfg; ++ struct resource *res; ++ int ret; ++ ++ phy_cfg = of_device_get_match_data(dev); ++ if (!phy_cfg) { ++ dev_err(dev, "no OF match data provided\n"); ++ return -EINVAL; ++ } ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(priv->mmio)) { ++ ret = PTR_ERR(priv->mmio); ++ return ret; ++ } ++ ++ priv->dev = dev; ++ priv->mode = PHY_NONE; ++ priv->cfg = phy_cfg; ++ ++ ret = rockchip_combphy_parse_dt(dev, priv); ++ if (ret) ++ return ret; ++ ++ ret = reset_control_assert(priv->phy_rst); ++ if (ret) { ++ dev_err(dev, "failed to reset phy\n"); ++ return ret; ++ } ++ ++ priv->phy = devm_phy_create(dev, NULL, &rochchip_combphy_ops); ++ if (IS_ERR(priv->phy)) { ++ dev_err(dev, "failed to create combphy\n"); ++ return PTR_ERR(priv->phy); ++ } ++ ++ dev_set_drvdata(dev, priv); ++ phy_set_drvdata(priv->phy, priv); ++ ++ phy_provider = devm_of_phy_provider_register(dev, rockchip_combphy_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) ++{ ++ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; ++ unsigned long rate; ++ u32 val; ++ ++ switch (priv->mode) { ++ case PHY_TYPE_PCIE: ++ /* Set SSC downward spread spectrum. */ ++ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, ++ PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, ++ PHYREG32); ++ ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_pcie, true); ++ break; ++ ++ case PHY_TYPE_USB3: ++ /* Set SSC downward spread spectrum. */ ++ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, ++ PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, ++ PHYREG32); ++ ++ /* Enable adaptive CTLE for USB3.0 Rx. */ ++ val = readl(priv->mmio + PHYREG15); ++ val |= PHYREG15_CTLE_EN; ++ writel(val, priv->mmio + PHYREG15); ++ ++ /* Set PLL KVCO fine tuning signals. */ ++ rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, ++ PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT, ++ PHYREG33); ++ ++ /* Enable controlling random jitter. */ ++ writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); ++ ++ /* Set PLL input clock divider 1/2. */ ++ rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, ++ PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT, ++ PHYREG6); ++ ++ writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); ++ writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); ++ ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_usb, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true); ++ break; ++ ++ case PHY_TYPE_SATA: ++ /* Enable adaptive CTLE for SATA Rx. */ ++ val = readl(priv->mmio + PHYREG15); ++ val |= PHYREG15_CTLE_EN; ++ writel(val, priv->mmio + PHYREG15); ++ /* ++ * Set tx_rterm=50ohm and rx_rterm=44ohm for SATA. ++ * 0: 60ohm, 8: 50ohm 15: 44ohm (by step abort 1ohm) ++ */ ++ val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT; ++ val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT; ++ writel(val, priv->mmio + PHYREG7); ++ ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_sata, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_sata, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_sata, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_sata, true); ++ rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true); ++ break; ++ ++ case PHY_TYPE_SGMII: ++ rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->sgmii_mode_set, true); ++ break; ++ ++ case PHY_TYPE_QSGMII: ++ rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_rate_sel, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true); ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->qsgmii_mode_set, true); ++ break; ++ ++ default: ++ dev_err(priv->dev, "incompatible PHY type\n"); ++ return -EINVAL; ++ } ++ ++ rate = clk_get_rate(priv->refclk); ++ ++ switch (rate) { ++ case REF_CLOCK_24MHz: ++ if (priv->mode == PHY_TYPE_USB3 || priv->mode == PHY_TYPE_SATA) { ++ /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz. */ ++ val = PHYREG15_SSC_CNT_VALUE << PHYREG15_SSC_CNT_SHIFT; ++ rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK, ++ val, PHYREG15); ++ ++ writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16); ++ } ++ break; ++ ++ case REF_CLOCK_25MHz: ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_25m, true); ++ break; ++ ++ case REF_CLOCK_100MHz: ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true); ++ if (priv->mode == PHY_TYPE_PCIE) { ++ /* PLL KVCO fine tuning. */ ++ val = PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT; ++ rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, ++ val, PHYREG33); ++ ++ /* Enable controlling random jitter. */ ++ writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); ++ ++ val = PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT; ++ rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, ++ val, PHYREG6); ++ ++ writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); ++ writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); ++ } else if (priv->mode == PHY_TYPE_SATA) { ++ /* downward spread spectrum +500ppm */ ++ val = PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT; ++ val |= PHYREG32_SSC_OFFSET_500PPM << PHYREG32_SSC_OFFSET_SHIFT; ++ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32); ++ } ++ break; ++ ++ default: ++ dev_err(priv->dev, "unsupported rate: %lu\n", rate); ++ return -EINVAL; ++ } ++ ++ if (priv->ext_refclk) { ++ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true); ++ if (priv->mode == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) { ++ val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT; ++ val |= PHYREG13_CKRCV_AMP0; ++ rockchip_combphy_updatel(priv, PHYREG13_RESISTER_MASK, val, PHYREG13); ++ ++ val = readl(priv->mmio + PHYREG14); ++ val |= PHYREG14_CKRCV_AMP1; ++ writel(val, priv->mmio + PHYREG14); ++ } ++ } ++ ++ if (priv->enable_ssc) { ++ val = readl(priv->mmio + PHYREG8); ++ val |= PHYREG8_SSC_EN; ++ writel(val, priv->mmio + PHYREG8); ++ } ++ ++ return 0; ++} ++ ++static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = { ++ /* pipe-phy-grf */ ++ .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 }, ++ .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 }, ++ .sgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x01 }, ++ .qsgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x21 }, ++ .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 }, ++ .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 }, ++ .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 }, ++ .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x01 }, ++ .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 }, ++ .pipe_phymode_sel = { 0x0008, 1, 1, 0x00, 0x01 }, ++ .pipe_rate_sel = { 0x0008, 2, 2, 0x00, 0x01 }, ++ .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 }, ++ .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 }, ++ .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 }, ++ .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 }, ++ .pipe_sel_usb = { 0x000c, 14, 13, 0x00, 0x01 }, ++ .pipe_sel_qsgmii = { 0x000c, 15, 13, 0x00, 0x07 }, ++ .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 }, ++ .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 }, ++ .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 }, ++ .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 }, ++ .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 }, ++ .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0119 }, ++ .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0040 }, ++ .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c3 }, ++ .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x4407 }, ++ /* pipe-grf */ ++ .pipe_con0_for_sata = { 0x0000, 15, 0, 0x00, 0x2220 }, ++ .pipe_xpcs_phy_ready = { 0x0040, 2, 2, 0x00, 0x01 }, ++}; ++ ++static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = { ++ .grfcfg = &rk3568_combphy_grfcfgs, ++ .combphy_cfg = rk3568_combphy_cfg, ++}; ++ ++static const struct of_device_id rockchip_combphy_of_match[] = { ++ { ++ .compatible = "rockchip,rk3568-naneng-combphy", ++ .data = &rk3568_combphy_cfgs, ++ }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, rockchip_combphy_of_match); ++ ++static struct platform_driver rockchip_combphy_driver = { ++ .probe = rockchip_combphy_probe, ++ .driver = { ++ .name = "rockchip-naneng-combphy", ++ .of_match_table = rockchip_combphy_of_match, ++ }, ++}; ++module_platform_driver(rockchip_combphy_driver); ++ ++MODULE_DESCRIPTION("Rockchip NANENG COMBPHY driver"); ++MODULE_LICENSE("GPL v2"); + +Add the core dt-node for the rk3568's naneng combo phys. + +Signed-off-by: Yifeng Zhao +Signed-off-by: Johan Jonker +--- + +Changes in v7: None +Changes in v5: +- remove apb reset + +Changes in v4: +- rename node name +- remove reset-names +- move #phy-cells +- add rockchip,rk3568-pipe-grf +- add rockchip,rk3568-pipe-phy-grf + +Changes in v3: +- Move pipe_phy_grf0 to rk3568.dtsi + +Changes in v2: +- Move phy0 to rk3568.dtsi + + arch/arm64/boot/dts/rockchip/rk3568.dtsi | 21 +++++++++++ + arch/arm64/boot/dts/rockchip/rk356x.dtsi | 47 ++++++++++++++++++++++++ + 2 files changed, 68 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi +index 2fd313a295f8..91a0b798b857 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi +@@ -8,6 +8,11 @@ + / { + compatible = "rockchip,rk3568"; + ++ pipe_phy_grf0: syscon@fdc70000 { ++ compatible = "rockchip,rk3568-pipe-phy-grf", "syscon"; ++ reg = <0x0 0xfdc70000 0x0 0x1000>; ++ }; ++ + qos_pcie3x1: qos@fe190080 { + compatible = "rockchip,rk3568-qos", "syscon"; + reg = <0x0 0xfe190080 0x0 0x20>; +@@ -71,6 +76,22 @@ + queue0 {}; + }; + }; ++ ++ combphy0: phy@fe820000 { ++ compatible = "rockchip,rk3568-naneng-combphy"; ++ reg = <0x0 0xfe820000 0x0 0x100>; ++ clocks = <&pmucru CLK_PCIEPHY0_REF>, ++ <&cru PCLK_PIPEPHY0>, ++ <&cru PCLK_PIPE>; ++ clock-names = "ref", "apb", "pipe"; ++ assigned-clocks = <&pmucru CLK_PCIEPHY0_REF>; ++ assigned-clock-rates = <100000000>; ++ resets = <&cru SRST_PIPEPHY0>; ++ rockchip,pipe-grf = <&pipegrf>; ++ rockchip,pipe-phy-grf = <&pipe_phy_grf0>; ++ #phy-cells = <1>; ++ status = "disabled"; ++ }; + }; + + &cpu0_opp_table { +diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +index a68033a23975..93f230f799f1 100644 +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -218,11 +218,26 @@ + }; + }; + ++ pipegrf: syscon@fdc50000 { ++ compatible = "rockchip,rk3568-pipe-grf", "syscon"; ++ reg = <0x0 0xfdc50000 0x0 0x1000>; ++ }; ++ + grf: syscon@fdc60000 { + compatible = "rockchip,rk3568-grf", "syscon", "simple-mfd"; + reg = <0x0 0xfdc60000 0x0 0x10000>; + }; + ++ pipe_phy_grf1: syscon@fdc80000 { ++ compatible = "rockchip,rk3568-pipe-phy-grf", "syscon"; ++ reg = <0x0 0xfdc80000 0x0 0x1000>; ++ }; ++ ++ pipe_phy_grf2: syscon@fdc90000 { ++ compatible = "rockchip,rk3568-pipe-phy-grf", "syscon"; ++ reg = <0x0 0xfdc90000 0x0 0x1000>; ++ }; ++ + pmucru: clock-controller@fdd00000 { + compatible = "rockchip,rk3568-pmucru"; + reg = <0x0 0xfdd00000 0x0 0x1000>; +@@ -1141,6 +1156,38 @@ + status = "disabled"; + }; + ++ combphy1: phy@fe830000 { ++ compatible = "rockchip,rk3568-naneng-combphy"; ++ reg = <0x0 0xfe830000 0x0 0x100>; ++ clocks = <&pmucru CLK_PCIEPHY1_REF>, ++ <&cru PCLK_PIPEPHY1>, ++ <&cru PCLK_PIPE>; ++ clock-names = "ref", "apb", "pipe"; ++ assigned-clocks = <&pmucru CLK_PCIEPHY1_REF>; ++ assigned-clock-rates = <100000000>; ++ resets = <&cru SRST_PIPEPHY1>; ++ rockchip,pipe-grf = <&pipegrf>; ++ rockchip,pipe-phy-grf = <&pipe_phy_grf1>; ++ #phy-cells = <1>; ++ status = "disabled"; ++ }; ++ ++ combphy2: phy@fe840000 { ++ compatible = "rockchip,rk3568-naneng-combphy"; ++ reg = <0x0 0xfe840000 0x0 0x100>; ++ clocks = <&pmucru CLK_PCIEPHY2_REF>, ++ <&cru PCLK_PIPEPHY2>, ++ <&cru PCLK_PIPE>; ++ clock-names = "ref", "apb", "pipe"; ++ assigned-clocks = <&pmucru CLK_PCIEPHY2_REF>; ++ assigned-clock-rates = <100000000>; ++ resets = <&cru SRST_PIPEPHY2>; ++ rockchip,pipe-grf = <&pipegrf>; ++ rockchip,pipe-phy-grf = <&pipe_phy_grf2>; ++ #phy-cells = <1>; ++ status = "disabled"; ++ }; ++ + pinctrl: pinctrl { + compatible = "rockchip,rk3568-pinctrl"; + rockchip,grf = <&grf>; diff --git a/patch/kernel/archive/rk35xx-5.17/rk356x-usb2-support.patch b/patch/kernel/archive/rk35xx-5.17/rk356x-usb2-support.patch new file mode 100644 index 0000000000..463bd81aa9 --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk356x-usb2-support.patch @@ -0,0 +1,230 @@ +Add the documentation for the rk3568-usb2phy-grf node, which is separate +from the usb2phy node on this chip. + +Signed-off-by: Peter Geis +Acked-by: Rob Herring +--- + Documentation/devicetree/bindings/soc/rockchip/grf.yaml | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +index dfebf425ca49..b2ba7bed89b2 100644 +--- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml ++++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +@@ -15,6 +15,7 @@ properties: + - items: + - enum: + - rockchip,rk3288-sgrf ++ - rockchip,rk3568-usb2phy-grf + - rockchip,rv1108-usbgrf + - const: syscon + - items: + +diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +index 46d9552f6028..2c2b1014e53b 100644 +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -204,6 +204,50 @@ gic: interrupt-controller@fd400000 { + msi-controller; + }; + ++ usb_host0_ehci: usb@fd800000 { ++ compatible = "generic-ehci"; ++ reg = <0x0 0xfd800000 0x0 0x40000>; ++ interrupts = ; ++ clocks = <&cru HCLK_USB2HOST0>, <&cru HCLK_USB2HOST0_ARB>, ++ <&cru PCLK_USB>; ++ phys = <&u2phy1_otg>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ ++ usb_host0_ohci: usb@fd840000 { ++ compatible = "generic-ohci"; ++ reg = <0x0 0xfd840000 0x0 0x40000>; ++ interrupts = ; ++ clocks = <&cru HCLK_USB2HOST0>, <&cru HCLK_USB2HOST0_ARB>, ++ <&cru PCLK_USB>; ++ phys = <&u2phy1_otg>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ ++ usb_host1_ehci: usb@fd880000 { ++ compatible = "generic-ehci"; ++ reg = <0x0 0xfd880000 0x0 0x40000>; ++ interrupts = ; ++ clocks = <&cru HCLK_USB2HOST1>, <&cru HCLK_USB2HOST1_ARB>, ++ <&cru PCLK_USB>; ++ phys = <&u2phy1_host>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ ++ usb_host1_ohci: usb@fd8c0000 { ++ compatible = "generic-ohci"; ++ reg = <0x0 0xfd8c0000 0x0 0x40000>; ++ interrupts = ; ++ clocks = <&cru HCLK_USB2HOST1>, <&cru HCLK_USB2HOST1_ARB>, ++ <&cru PCLK_USB>; ++ phys = <&u2phy1_host>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ + pmugrf: syscon@fdc20000 { + compatible = "rockchip,rk3568-pmugrf", "syscon", "simple-mfd"; + reg = <0x0 0xfdc20000 0x0 0x10000>; +@@ -219,6 +263,16 @@ grf: syscon@fdc60000 { + reg = <0x0 0xfdc60000 0x0 0x10000>; + }; + ++ usb2phy0_grf: syscon@fdca0000 { ++ compatible = "rockchip,rk3568-usb2phy-grf", "syscon"; ++ reg = <0x0 0xfdca0000 0x0 0x8000>; ++ }; ++ ++ usb2phy1_grf: syscon@fdca8000 { ++ compatible = "rockchip,rk3568-usb2phy-grf", "syscon"; ++ reg = <0x0 0xfdca8000 0x0 0x8000>; ++ }; ++ + pmucru: clock-controller@fdd00000 { + compatible = "rockchip,rk3568-pmucru"; + reg = <0x0 0xfdd00000 0x0 0x1000>; +@@ -1077,6 +1131,50 @@ pwm15: pwm@fe700030 { + status = "disabled"; + }; + ++ u2phy0: usb2phy@fe8a0000 { ++ compatible = "rockchip,rk3568-usb2phy"; ++ reg = <0x0 0xfe8a0000 0x0 0x10000>; ++ clocks = <&pmucru CLK_USBPHY0_REF>; ++ clock-names = "phyclk"; ++ clock-output-names = "clk_usbphy0_480m"; ++ interrupts = ; ++ rockchip,usbgrf = <&usb2phy0_grf>; ++ #clock-cells = <0>; ++ status = "disabled"; ++ ++ u2phy0_host: host-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ u2phy0_otg: otg-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ ++ u2phy1: usb2phy@fe8b0000 { ++ compatible = "rockchip,rk3568-usb2phy"; ++ reg = <0x0 0xfe8b0000 0x0 0x10000>; ++ clocks = <&pmucru CLK_USBPHY1_REF>; ++ clock-names = "phyclk"; ++ clock-output-names = "clk_usbphy1_480m"; ++ interrupts = ; ++ rockchip,usbgrf = <&usb2phy1_grf>; ++ #clock-cells = <0>; ++ status = "disabled"; ++ ++ u2phy1_host: host-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ u2phy1_otg: otg-port { ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ + pinctrl: pinctrl { + compatible = "rockchip,rk3568-pinctrl"; + rockchip,grf = <&grf>; + + +Add the nodes and regulators to enable usb2 support on the Quartz64 +Model A. + +Signed-off-by: Peter Geis +Tested-by: Michael Riesch +--- + .../boot/dts/rockchip/rk3566-quartz64-a.dts | 52 +++++++++++++++++++ + 1 file changed, 52 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +index 4d4b2a301b1a..e5a70ff4e920 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +@@ -124,6 +124,22 @@ vcc5v0_usb: vcc5v0_usb { + vin-supply = <&vcc12v_dcin>; + }; + ++ /* all four ports are controlled by one gpio ++ * the host ports are sourced from vcc5v0_usb ++ * the otg port is sourced from vcc5v0_midu ++ */ ++ vcc5v0_usb20_host: vcc5v0_usb20_host { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc5v0_usb20_host"; ++ enable-active-high; ++ gpio = <&gpio4 RK_PB5 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&vcc5v0_usb20_host_en>; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ vin-supply = <&vcc5v0_usb>; ++ }; ++ + vcc3v3_sd: vcc3v3_sd { + compatible = "regulator-fixed"; + enable-active-low; +@@ -477,6 +493,12 @@ pmic_int_l: pmic-int-l { + }; + }; + ++ usb2 { ++ vcc5v0_usb20_host_en: vcc5v0-usb20-host-en { ++ rockchip,pins = <4 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ + vcc_sd { + vcc_sd_h: vcc-sd-h { + rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>; +@@ -546,3 +568,33 @@ bluetooth { + &uart2 { + status = "okay"; + }; ++ ++&u2phy1_host { ++ phy-supply = <&vcc5v0_usb20_host>; ++ status = "okay"; ++}; ++ ++&u2phy1_otg { ++ phy-supply = <&vcc5v0_usb20_host>; ++ status = "okay"; ++}; ++ ++&u2phy1 { ++ status = "okay"; ++}; ++ ++&usb_host0_ehci { ++ status = "okay"; ++}; ++ ++&usb_host0_ohci { ++ status = "okay"; ++}; ++ ++&usb_host1_ehci { ++ status = "okay"; ++}; ++ ++&usb_host1_ohci { ++ status = "okay"; ++}; diff --git a/patch/kernel/archive/rk35xx-5.17/rk356x-usb3.patch b/patch/kernel/archive/rk35xx-5.17/rk356x-usb3.patch new file mode 100644 index 0000000000..607374bf70 --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk356x-usb3.patch @@ -0,0 +1,506 @@ +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -247,6 +247,98 @@ + }; + }; + ++ sata1: sata@fc400000 { ++ compatible = "snps,dwc-ahci"; ++ reg = <0 0xfc400000 0 0x1000>; ++ clocks = <&cru ACLK_SATA1>, <&cru CLK_SATA1_PMALIVE>, ++ <&cru CLK_SATA1_RXOOB>; ++ clock-names = "sata", "pmalive", "rxoob"; ++ interrupts = ; ++ interrupt-names = "hostc"; ++ phys = <&combphy1 PHY_TYPE_SATA>; ++ phy-names = "sata-phy"; ++ ports-implemented = <0x1>; ++ power-domains = <&power RK3568_PD_PIPE>; ++ status = "disabled"; ++ }; ++ ++ sata2: sata@fc800000 { ++ compatible = "snps,dwc-ahci"; ++ reg = <0 0xfc800000 0 0x1000>; ++ clocks = <&cru ACLK_SATA2>, <&cru CLK_SATA2_PMALIVE>, ++ <&cru CLK_SATA2_RXOOB>; ++ clock-names = "sata", "pmalive", "rxoob"; ++ interrupts = ; ++ interrupt-names = "hostc"; ++ phys = <&combphy2 PHY_TYPE_SATA>; ++ phy-names = "sata-phy"; ++ ports-implemented = <0x1>; ++ power-domains = <&power RK3568_PD_PIPE>; ++ status = "disabled"; ++ }; ++ ++ usbdrd30: usbdrd { ++ compatible = "rockchip,rk3399-dwc3", "snps,dwc3"; ++ clocks = <&cru CLK_USB3OTG0_REF>, <&cru CLK_USB3OTG0_SUSPEND>, ++ <&cru ACLK_USB3OTG0>, <&cru PCLK_PIPE>; ++ clock-names = "ref_clk", "suspend_clk", ++ "bus_clk", "pipe_clk"; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ status = "disabled"; ++ ++ usbdrd_dwc3: dwc3@fcc00000 { ++ compatible = "snps,dwc3"; ++ reg = <0x0 0xfcc00000 0x0 0x400000>; ++ interrupts = ; ++ dr_mode = "host"; ++ phy_type = "utmi_wide"; ++ power-domains = <&power RK3568_PD_PIPE>; ++ resets = <&cru SRST_USB3OTG0>; ++ reset-names = "usb3-otg"; ++ snps,dis_enblslpm_quirk; ++ snps,dis-u2-freeclk-exists-quirk; ++ snps,dis-del-phy-power-chg-quirk; ++ snps,dis-tx-ipgap-linecheck-quirk; ++ snps,xhci-trb-ent-quirk; ++ status = "disabled"; ++ }; ++ }; ++ ++ usbhost30: usbhost { ++ compatible = "rockchip,rk3399-dwc3", "snps,dwc3"; ++ clocks = <&cru CLK_USB3OTG1_REF>, <&cru CLK_USB3OTG1_SUSPEND>, ++ <&cru ACLK_USB3OTG1>, <&cru PCLK_PIPE>; ++ clock-names = "ref_clk", "suspend_clk", ++ "bus_clk", "pipe_clk"; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ assigned-clocks = <&cru CLK_PCIEPHY1_REF>; ++ assigned-clock-rates = <25000000>; ++ ranges; ++ status = "disabled"; ++ ++ usbhost_dwc3: dwc3@fd000000 { ++ compatible = "snps,dwc3"; ++ reg = <0x0 0xfd000000 0x0 0x400000>; ++ interrupts = ; ++ dr_mode = "host"; ++ phys = <&u2phy0_host>, <&combphy1 PHY_TYPE_USB3>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ phy_type = "utmi_wide"; ++ power-domains = <&power RK3568_PD_PIPE>; ++ resets = <&cru SRST_USB3OTG1>; ++ reset-names = "usb3-host"; ++ snps,dis_enblslpm_quirk; ++ snps,dis-u2-freeclk-exists-quirk; ++ snps,dis_u2_susphy_quirk; ++ snps,dis-del-phy-power-chg-quirk; ++ snps,dis-tx-ipgap-linecheck-quirk; ++ status = "disabled"; ++ }; ++ }; ++ + gic: interrupt-controller@fd400000 { + compatible = "arm,gic-v3"; + reg = <0x0 0xfd400000 0 0x10000>, /* GICD */ +@@ -365,6 +472,7 @@ + clocks = <&pmucru SCLK_UART0>, <&pmucru PCLK_UART0>; + clock-names = "baudclk", "apb_pclk"; + dmas = <&dmac0 0>, <&dmac0 1>; ++ dma-names = "tx", "rx"; + pinctrl-0 = <&uart0_xfer>; + pinctrl-names = "default"; + reg-io-width = <4>; +@@ -770,6 +879,61 @@ + qos_vop_m1: qos@fe1a8100 { + compatible = "rockchip,rk3568-qos", "syscon"; + reg = <0x0 0xfe1a8100 0x0 0x20>; ++ }; ++ ++ pcie2x1: pcie@fe260000 { ++ compatible = "rockchip,rk3568-pcie"; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ bus-range = <0x0 0xf>; ++ assigned-clocks = <&cru ACLK_PCIE20_MST>, <&cru ACLK_PCIE20_SLV>, ++ <&cru ACLK_PCIE20_DBI>, <&cru PCLK_PCIE20>, ++ <&cru CLK_PCIE20_AUX_NDFT>; ++ clocks = <&cru ACLK_PCIE20_MST>, <&cru ACLK_PCIE20_SLV>, ++ <&cru ACLK_PCIE20_DBI>, <&cru PCLK_PCIE20>, ++ <&cru CLK_PCIE20_AUX_NDFT>; ++ clock-names = "aclk_mst", "aclk_slv", ++ "aclk_dbi", "pclk", "aux"; ++ device_type = "pci"; ++ interrupts = , ++ , ++ , ++ , ++ ; ++ interrupt-names = "sys", "pmc", "msi", "legacy", "err"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie_intc 0>, ++ <0 0 0 2 &pcie_intc 1>, ++ <0 0 0 3 &pcie_intc 2>, ++ <0 0 0 4 &pcie_intc 3>; ++ linux,pci-domain = <0>; ++ num-ib-windows = <6>; ++ num-ob-windows = <2>; ++ max-link-speed = <2>; ++ msi-map = <0x0 &gic 0x0 0x1000>; ++ num-lanes = <1>; ++ phys = <&combphy2 PHY_TYPE_PCIE>; ++ phy-names = "pcie-phy"; ++ power-domains = <&power RK3568_PD_PIPE>; ++ reg = <0x3 0xc0000000 0x0 0x400000>, ++ <0x0 0xfe260000 0x0 0x10000>, ++ <0x3 0x3f800000 0x0 0x800000>; ++ ranges = <0x1000000 0x0 0x7f700000 0x3 0x3f700000 0x0 0x00100000 ++ 0x2000000 0x0 0x40000000 0x3 0x00000000 0x0 0x3f700000>; ++ reg-names = "dbi", "apb", "config"; ++ resets = <&cru SRST_PCIE20_POWERUP>; ++ reset-names = "pipe"; ++ status = "disabled"; ++ ++ pcie_intc: legacy-interrupt-controller { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ interrupt-parent = <&gic>; ++ interrupts = ; ++ }; ++ + }; + + sdmmc0: mmc@fe2b0000 { +@@ -797,6 +961,17 @@ + max-frequency = <150000000>; + resets = <&cru SRST_SDMMC1>; + reset-names = "reset"; ++ status = "disabled"; ++ }; ++ ++ sfc: spi@fe300000 { ++ compatible = "rockchip,sfc"; ++ reg = <0x0 0xfe300000 0x0 0x4000>; ++ interrupts = ; ++ clocks = <&cru SCLK_SFC>, <&cru HCLK_SFC>; ++ clock-names = "clk_sfc", "hclk_sfc"; ++ pinctrl-0 = <&fspi_pins>; ++ pinctrl-names = "default"; + status = "disabled"; + }; + +@@ -971,6 +1146,7 @@ + clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>; + clock-names = "baudclk", "apb_pclk"; + dmas = <&dmac0 2>, <&dmac0 3>; ++ dma-names = "tx", "rx"; + pinctrl-0 = <&uart1m0_xfer>; + pinctrl-names = "default"; + reg-io-width = <4>; +@@ -985,6 +1161,7 @@ + clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>; + clock-names = "baudclk", "apb_pclk"; + dmas = <&dmac0 4>, <&dmac0 5>; ++ dma-names = "tx", "rx"; + pinctrl-0 = <&uart2m0_xfer>; + pinctrl-names = "default"; + reg-io-width = <4>; +@@ -999,6 +1176,7 @@ + clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>; + clock-names = "baudclk", "apb_pclk"; + dmas = <&dmac0 6>, <&dmac0 7>; ++ dma-names = "tx", "rx"; + pinctrl-0 = <&uart3m0_xfer>; + pinctrl-names = "default"; + reg-io-width = <4>; +@@ -1013,6 +1191,7 @@ + clocks = <&cru SCLK_UART4>, <&cru PCLK_UART4>; + clock-names = "baudclk", "apb_pclk"; + dmas = <&dmac0 8>, <&dmac0 9>; ++ dma-names = "tx", "rx"; + pinctrl-0 = <&uart4m0_xfer>; + pinctrl-names = "default"; + reg-io-width = <4>; +@@ -1027,6 +1206,7 @@ + clocks = <&cru SCLK_UART5>, <&cru PCLK_UART5>; + clock-names = "baudclk", "apb_pclk"; + dmas = <&dmac0 10>, <&dmac0 11>; ++ dma-names = "tx", "rx"; + pinctrl-0 = <&uart5m0_xfer>; + pinctrl-names = "default"; + reg-io-width = <4>; +@@ -1041,6 +1221,7 @@ + clocks = <&cru SCLK_UART6>, <&cru PCLK_UART6>; + clock-names = "baudclk", "apb_pclk"; + dmas = <&dmac0 12>, <&dmac0 13>; ++ dma-names = "tx", "rx"; + pinctrl-0 = <&uart6m0_xfer>; + pinctrl-names = "default"; + reg-io-width = <4>; +@@ -1055,6 +1236,7 @@ + clocks = <&cru SCLK_UART7>, <&cru PCLK_UART7>; + clock-names = "baudclk", "apb_pclk"; + dmas = <&dmac0 14>, <&dmac0 15>; ++ dma-names = "tx", "rx"; + pinctrl-0 = <&uart7m0_xfer>; + pinctrl-names = "default"; + reg-io-width = <4>; +@@ -1069,6 +1251,7 @@ + clocks = <&cru SCLK_UART8>, <&cru PCLK_UART8>; + clock-names = "baudclk", "apb_pclk"; + dmas = <&dmac0 16>, <&dmac0 17>; ++ dma-names = "tx", "rx"; + pinctrl-0 = <&uart8m0_xfer>; + pinctrl-names = "default"; + reg-io-width = <4>; +@@ -1083,6 +1266,7 @@ + clocks = <&cru SCLK_UART9>, <&cru PCLK_UART9>; + clock-names = "baudclk", "apb_pclk"; + dmas = <&dmac0 18>, <&dmac0 19>; ++ dma-names = "tx", "rx"; + pinctrl-0 = <&uart9m0_xfer>; + pinctrl-names = "default"; + reg-io-width = <4>; + +--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi +@@ -7,6 +7,21 @@ + + / { + compatible = "rockchip,rk3568"; ++ ++ sata0: sata@fc000000 { ++ compatible = "snps,dwc-ahci"; ++ reg = <0 0xfc000000 0 0x1000>; ++ clocks = <&cru ACLK_SATA0>, <&cru CLK_SATA0_PMALIVE>, ++ <&cru CLK_SATA0_RXOOB>; ++ clock-names = "sata", "pmalive", "rxoob"; ++ interrupts = ; ++ interrupt-names = "hostc"; ++ phys = <&combphy0 PHY_TYPE_SATA>; ++ phy-names = "sata-phy"; ++ ports-implemented = <0x1>; ++ power-domains = <&power RK3568_PD_PIPE>; ++ status = "disabled"; ++ }; + + qos_pcie3x1: qos@fe190080 { + compatible = "rockchip,rk3568-qos", "syscon"; +@@ -78,6 +109,10 @@ + opp-hz = /bits/ 64 <1992000000>; + opp-microvolt = <1150000 1150000 1150000>; + }; ++}; ++ ++&pipegrf { ++ compatible = "rockchip,rk3568-pipegrf", "syscon"; + }; + + &power { +@@ -95,3 +130,8 @@ + #power-domain-cells = <0>; + }; + }; ++ ++&usbdrd_dwc3 { ++ phys = <&u2phy0_otg>, <&combphy0 PHY_TYPE_USB3>; ++ phy-names = "usb2-phy", "usb3-phy"; ++}; + +--- a/arch/arm64/boot/dts/rockchip/rk3566.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3566.dtsi +@@ -4,6 +4,10 @@ + + / { + compatible = "rockchip,rk3566"; ++}; ++ ++&pipegrf { ++ compatible = "rockchip,rk3566-pipegrf", "syscon"; + }; + + &power { +@@ -18,3 +22,11 @@ + #power-domain-cells = <0>; + }; + }; ++ ++&usbdrd_dwc3 { ++ phys = <&u2phy0_otg>; ++ phy-names = "usb2-phy"; ++ extcon = <&u2phy0>; ++ maximum-speed = "high-speed"; ++ snps,dis_u2_susphy_quirk; ++}; + +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -258,6 +258,7 @@ + /* Global User Control 1 Register */ + #define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31) + #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) ++#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26) + #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) + #define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) + + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -1087,6 +1087,10 @@ + + if (dwc->parkmode_disable_ss_quirk) + reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS; ++ ++ if (dwc->maximum_speed == USB_SPEED_HIGH || ++ dwc->maximum_speed == USB_SPEED_FULL) ++ reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK; + + dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); + } + +--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c ++++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c +@@ -10,9 +10,12 @@ + + #include + #include ++#include ++#include + #include + #include + #include ++#include + #include + #include + #include +@@ -36,10 +39,12 @@ + #define PCIE_LINKUP (PCIE_SMLH_LINKUP | PCIE_RDLH_LINKUP) + #define PCIE_L0S_ENTRY 0x11 + #define PCIE_CLIENT_GENERAL_CONTROL 0x0 ++#define PCIE_CLIENT_INTR_MASK_LEGACY 0x1c + #define PCIE_CLIENT_GENERAL_DEBUG 0x104 +-#define PCIE_CLIENT_HOT_RESET_CTRL 0x180 ++#define PCIE_CLIENT_HOT_RESET_CTRL 0x180 + #define PCIE_CLIENT_LTSSM_STATUS 0x300 +-#define PCIE_LTSSM_ENABLE_ENHANCE BIT(4) ++#define PCIE_LEGACY_INT_ENABLE GENMASK(7, 0) ++#define PCIE_LTSSM_ENABLE_ENHANCE BIT(4) + #define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0) + + struct rockchip_pcie { +@@ -51,6 +56,7 @@ + struct reset_control *rst; + struct gpio_desc *rst_gpio; + struct regulator *vpcie3v3; ++ struct irq_domain *irq_domain; + }; + + static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, +@@ -63,6 +69,68 @@ + u32 val, u32 reg) + { + writel_relaxed(val, rockchip->apb_base + reg); ++} ++ ++static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc) ++{ ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct rockchip_pcie *rockchip = irq_desc_get_handler_data(desc); ++ struct device *dev = rockchip->pci.dev; ++ u32 reg; ++ u32 hwirq; ++ u32 virq; ++ ++ chained_irq_enter(chip, desc); ++ ++ reg = rockchip_pcie_readl_apb(rockchip, 0x8); ++ ++ while (reg) { ++ hwirq = ffs(reg) - 1; ++ reg &= ~BIT(hwirq); ++ ++ virq = irq_find_mapping(rockchip->irq_domain, hwirq); ++ if (virq) ++ generic_handle_irq(virq); ++ else ++ dev_err(dev, "unexpected IRQ, INT%d\n", hwirq); ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); ++ irq_set_chip_data(irq, domain->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops intx_domain_ops = { ++ .map = rockchip_pcie_intx_map, ++}; ++ ++static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip) ++{ ++ struct device *dev = rockchip->pci.dev; ++ struct device_node *intc; ++ ++ intc = of_get_child_by_name(dev->of_node, "legacy-interrupt-controller"); ++ if (!intc) { ++ dev_err(dev, "missing child interrupt-controller node\n"); ++ return -EINVAL; ++ } ++ ++ rockchip->irq_domain = irq_domain_add_linear(intc, PCI_NUM_INTX, ++ &intx_domain_ops, rockchip); ++ of_node_put(intc); ++ if (!rockchip->irq_domain) { ++ dev_err(dev, "failed to get a INTx IRQ domain\n"); ++ return -EINVAL; ++ } ++ ++ return 0; + } + + static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip) +@@ -111,9 +179,27 @@ + { + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); +- u32 val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE); ++ struct device *dev = rockchip->pci.dev; ++ int irq, ret; ++ u32 val; ++ ++ irq = of_irq_get_byname(dev->of_node, "legacy"); ++ if (irq < 0) ++ return irq; ++ ++ irq_set_chained_handler_and_data(irq, rockchip_pcie_legacy_int_handler, rockchip); ++ ++ ret = rockchip_pcie_init_irq_domain(rockchip); ++ if (ret < 0) ++ dev_err(dev, "failed to init irq domain\n"); ++ ++ /* enable legacy interrupts */ ++ val = HIWORD_UPDATE_BIT(PCIE_LEGACY_INT_ENABLE); ++ val &= ~PCIE_LEGACY_INT_ENABLE; ++ rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_INTR_MASK_LEGACY); + + /* LTSSM enable control mode */ ++ val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE); + rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL); + + rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_RC_MODE, +@@ -214,6 +300,10 @@ + + rockchip->pci.dev = dev; + rockchip->pci.ops = &dw_pcie_ops; ++ ++ ret = dma_set_mask(rockchip->pci.dev, DMA_BIT_MASK(32)); ++ if (ret) ++ dev_warn(rockchip->pci.dev, "Failed to set DMA mask to 32-bit. Devices with only 32-bit MSI support may not work properly\n"); + + pp = &rockchip->pci.pp; + pp->ops = &rockchip_pcie_host_ops; + diff --git a/patch/kernel/archive/rk35xx-5.17/rk356x-vpu.patch b/patch/kernel/archive/rk35xx-5.17/rk356x-vpu.patch new file mode 100644 index 0000000000..491f196ab9 --- /dev/null +++ b/patch/kernel/archive/rk35xx-5.17/rk356x-vpu.patch @@ -0,0 +1,32 @@ +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -617,6 +617,28 @@ + #cooling-cells = <2>; + power-domains = <&power RK3568_PD_GPU>; + status = "disabled"; ++ }; ++ ++ vpu: video-codec@fdea0400 { ++ compatible = "rockchip,rk3328-vpu"; ++ reg = <0x0 0xfdea0000 0x0 0x800>; ++ interrupts = ; ++ interrupt-names = "vdpu"; ++ clocks = <&cru ACLK_VPU>, <&cru HCLK_VPU>; ++ clock-names = "aclk", "hclk"; ++ iommus = <&vdpu_mmu>; ++ power-domains = <&power RK3568_PD_VPU>; ++ }; ++ ++ vdpu_mmu: iommu@fdea0800 { ++ compatible = "rockchip,rk3568-iommu"; ++ reg = <0x0 0xfdea0800 0x0 0x40>; ++ interrupts = ; ++ interrupt-names = "vdpu_mmu"; ++ clock-names = "aclk", "iface"; ++ clocks = <&cru ACLK_VPU>, <&cru HCLK_VPU>; ++ power-domains = <&power RK3568_PD_VPU>; ++ #iommu-cells = <0>; + }; + + sdmmc2: mmc@fe000000 { + diff --git a/patch/kernel/rk35xx-edge b/patch/kernel/rk35xx-edge index 725efd44a4..188dcb28fe 120000 --- a/patch/kernel/rk35xx-edge +++ b/patch/kernel/rk35xx-edge @@ -1 +1 @@ -archive/rk35xx-5.16 \ No newline at end of file +archive/rk35xx-5.17 \ No newline at end of file