From 422fcf506834a99552df9b4a9fd3fac429b459f0 Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Wed, 19 Feb 2025 16:12:45 +0800 Subject: [PATCH 01/14] soc: rockchip: cpuinfo: Add support to parse 'cpu-code1' Change-Id: I080efaae76c28fd255c314fc9033efcc02457d4d Signed-off-by: Finley Xiao --- drivers/soc/rockchip/rockchip-cpuinfo.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/soc/rockchip/rockchip-cpuinfo.c b/drivers/soc/rockchip/rockchip-cpuinfo.c index ec1921b0d2ef..c37a0aa43a3c 100644 --- a/drivers/soc/rockchip/rockchip-cpuinfo.c +++ b/drivers/soc/rockchip/rockchip-cpuinfo.c @@ -29,20 +29,34 @@ static int rockchip_cpuinfo_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct nvmem_cell *cell; unsigned char *efuse_buf, buf[16]; + bool is_cpu_code1_valid = false; size_t len = 0; int i; - cell = nvmem_cell_get(dev, "cpu-code"); + cell = nvmem_cell_get(dev, "cpu-code1"); if (!IS_ERR(cell)) { efuse_buf = nvmem_cell_read(cell, &len); nvmem_cell_put(cell); if (IS_ERR(efuse_buf)) return PTR_ERR(efuse_buf); - - if (len == 2) + if (len == 2 && efuse_buf[0] && efuse_buf[1]) { rockchip_set_cpu((efuse_buf[0] << 8 | efuse_buf[1])); + is_cpu_code1_valid = true; + } kfree(efuse_buf); } + if (!is_cpu_code1_valid) { + cell = nvmem_cell_get(dev, "cpu-code"); + if (!IS_ERR(cell)) { + efuse_buf = nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + if (IS_ERR(efuse_buf)) + return PTR_ERR(efuse_buf); + if (len == 2) + rockchip_set_cpu((efuse_buf[0] << 8 | efuse_buf[1])); + kfree(efuse_buf); + } + } cell = nvmem_cell_get(dev, "cpu-version"); if (!IS_ERR(cell)) { From aa5ca65c011157a6bf02f7f0169e2ad4b4c0933c Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Wed, 19 Feb 2025 14:50:33 +0800 Subject: [PATCH 02/14] soc: rockchip: cpuinfo: Add support for rk3518 Change-Id: I3061709de83521c6c98df68ba12422e2523c88ab Signed-off-by: Finley Xiao --- include/linux/rockchip/cpu.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/linux/rockchip/cpu.h b/include/linux/rockchip/cpu.h index 0cd6f673c4d9..c6be2c96ed84 100644 --- a/include/linux/rockchip/cpu.h +++ b/include/linux/rockchip/cpu.h @@ -26,6 +26,7 @@ #define ROCKCHIP_CPU_RK312X 0x31260000 #define ROCKCHIP_CPU_RK3288 0x32880000 #define ROCKCHIP_CPU_RK3308 0x33080000 +#define ROCKCHIP_CPU_RK3518 0x35180000 #define ROCKCHIP_CPU_RK3528 0x35280000 #define ROCKCHIP_CPU_RK3566 0x35660000 #define ROCKCHIP_CPU_RK3567 0x35670000 @@ -185,8 +186,16 @@ static inline bool cpu_is_rk3528(void) return of_machine_is_compatible("rockchip,rk3528") || of_machine_is_compatible("rockchip,rk3528a"); } + +static inline bool cpu_is_rk3518(void) +{ + if (rockchip_soc_id) + return (rockchip_soc_id & ROCKCHIP_CPU_MASK) == ROCKCHIP_CPU_RK3518; + return of_machine_is_compatible("rockchip,rk3518"); +} #else static inline bool cpu_is_rk3528(void) { return false; } +static inline bool cpu_is_rk3518(void) { return false; } #endif #if defined(CONFIG_CPU_RK3568) @@ -232,6 +241,7 @@ static inline bool cpu_is_rk3568(void) { return false; } #define ROCKCHIP_SOC_RK3308 (ROCKCHIP_CPU_RK3308 | 0x00) #define ROCKCHIP_SOC_RK3308B (ROCKCHIP_CPU_RK3308 | 0x01) #define ROCKCHIP_SOC_RK3308BS (ROCKCHIP_CPU_RK3308 | 0x02) +#define ROCKCHIP_SOC_RK3518 (ROCKCHIP_CPU_RK3518 | 0x00) #define ROCKCHIP_SOC_RK3528 (ROCKCHIP_CPU_RK3528 | 0x00) #define ROCKCHIP_SOC_RK3528A (ROCKCHIP_CPU_RK3528 | 0x01) #define ROCKCHIP_SOC_RK3566 (ROCKCHIP_CPU_RK3566 | 0x00) @@ -263,6 +273,7 @@ ROCKCHIP_SOC(RK3288, rk3288w, RK3288W) ROCKCHIP_SOC(RK3308, rk3308, RK3308) ROCKCHIP_SOC(RK3308, rk3308b, RK3308B) ROCKCHIP_SOC(RK3308, rk3308bs, RK3308BS) +ROCKCHIP_SOC(RK3518, rk3518, RK3518) ROCKCHIP_SOC(RK3528, rk3528, RK3528) ROCKCHIP_SOC(RK3528, rk3528a, RK3528A) ROCKCHIP_SOC(RK3568, rk3566, RK3566) From de1f9cd3b4423b3c0e926090ca73c2ead7ef8d7d Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Mon, 24 Feb 2025 19:45:44 +0800 Subject: [PATCH 03/14] cpufreq: dt-platdev: Add rk3518 project into blacklist Change-Id: Ic18aa909ded2c4820036ea4bbaf6a60510a69078 Signed-off-by: Finley Xiao --- drivers/cpufreq/cpufreq-dt-platdev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index 12ae0f5308e6..a6f5bd199701 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -158,6 +158,7 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "rockchip,rk3368", }, { .compatible = "rockchip,rk3399", }, { .compatible = "rockchip,rk3399pro", }, + { .compatible = "rockchip,rk3518", }, { .compatible = "rockchip,rk3528", }, { .compatible = "rockchip,rk3562", }, { .compatible = "rockchip,rk3566", }, From 5c5445c865a25252a9978750ea19bf6f96ae2e1d Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Tue, 7 Jan 2025 11:49:56 +0800 Subject: [PATCH 04/14] arm64: dts: rockchip: add core dtsi for RK3518 Soc RK3518 is a Soc base on Rockchip RK3528, the cpu freq is lower than RK3528 and remove PCIE/RGMII supported. Change-Id: I9334959b598ece349dfce7e2b922cf91562c61ac Signed-off-by: Sandy Huang Signed-off-by: Finley Xiao --- arch/arm64/boot/dts/rockchip/rk3518.dtsi | 161 +++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 arch/arm64/boot/dts/rockchip/rk3518.dtsi diff --git a/arch/arm64/boot/dts/rockchip/rk3518.dtsi b/arch/arm64/boot/dts/rockchip/rk3518.dtsi new file mode 100644 index 000000000000..973e69654dcc --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3518.dtsi @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + */ + +#include "rk3528.dtsi" + +/ { + compatible = "rockchip,rk3518"; + + aliases { + /delete-property/ ethernet1; + }; + + /delete-node/ cpuinfo; + cpuinfo { + compatible = "rockchip,cpuinfo"; + nvmem-cells = <&otp_id>, <&otp_cpu_version>, + <&cpu_code>, <&cpu_code1>; + nvmem-cell-names = "id", "cpu-version", + "cpu-code", "cpu-code1"; + }; +}; + +&cpu0_opp_table { + rockchip,video-4k-freq = <1200000>; + rockchip,pvtm-voltage-sel = < + 0 1420 0 + 1410 1444 1 + 1445 1474 2 + 1475 1509 3 + 1510 1544 4 + 1545 1579 5 + 1580 1619 6 + 1620 1689 7 + 1690 1744 8 + 1745 1799 9 + 1800 9999 10 + >; + rockchip,pvtm-pvtpll; + rockchip,pvtm-offset = <0x18>; + rockchip,pvtm-sample-time = <1100>; + rockchip,pvtm-freq = <1416000>; + rockchip,pvtm-volt = <950000>; + rockchip,pvtm-ref-temp = <40>; + rockchip,pvtm-temp-prop = <0 0>; + rockchip,pvtm-thermal-zone = "soc-thermal"; + rockchip,grf = <&grf>; + rockchip,temp-hysteresis = <5000>; + rockchip,low-temp = <10000>; + rockchip,low-temp-min-volt = <900000>; + + /delete-node/ opp-408000000; + /delete-node/ opp-600000000; + /delete-node/ opp-816000000; + /delete-node/ opp-1008000000; + /delete-node/ opp-1200000000; + /delete-node/ opp-1416000000; + /delete-node/ opp-1608000000; + /delete-node/ opp-1800000000; + /delete-node/ opp-2016000000; + + opp-1200000000 { + opp-hz = /bits/ 64 <1200000000>; + opp-microvolt = <900000 900000 1000000>; + clock-latency-ns = <40000>; + }; + opp-1416000000 { + opp-hz = /bits/ 64 <1416000000>; + opp-microvolt = <900000 900000 1000000>; + opp-microvolt-L0 = <962500 962500 1000000>; + opp-microvolt-L1 = <950000 950000 1000000>; + opp-microvolt-L2 = <937500 937500 1000000>; + opp-microvolt-L3 = <925000 925000 1000000>; + opp-microvolt-L4 = <912500 912500 1000000>; + clock-latency-ns = <40000>; + }; +}; + +&dmc_opp_table { + /delete-node/ opp-324000000; + /delete-node/ opp-666000000; + /delete-node/ opp-920000000; + /delete-node/ opp-1056000000; + + opp-324000000 { + opp-hz = /bits/ 64 <324000000>; + opp-microvolt = <900000 900000 1000000>; + }; + opp-666000000 { + opp-hz = /bits/ 64 <666000000>; + opp-microvolt = <900000 900000 1000000>; + }; + opp-780000000 { + opp-hz = /bits/ 64 <780000000>; + opp-microvolt = <900000 900000 1000000>; + }; + opp-1056000000 { + opp-hz = /bits/ 64 <1056000000>; + opp-microvolt = <900000 900000 1000000>; + }; +}; + +&gpu_opp_table{ + rockchip,pvtm-voltage-sel = < + 0 809 0 + 810 830 1 + 831 850 2 + 851 880 3 + 881 890 4 + 891 910 5 + 911 930 6 + 931 950 7 + 951 970 8 + 971 995 9 + 1000 9999 10 + >; + rockchip,pvtm-pvtpll; + rockchip,pvtm-offset = <0x10018>; + rockchip,pvtm-sample-time = <1100>; + rockchip,pvtm-freq = <800000>; + rockchip,pvtm-volt = <950000>; + rockchip,pvtm-ref-temp = <40>; + rockchip,pvtm-temp-prop = <0 0>; + rockchip,pvtm-thermal-zone = "soc-thermal"; + rockchip,grf = <&grf>; + rockchip,temp-hysteresis = <5000>; + rockchip,low-temp = <10000>; + rockchip,low-temp-min-volt = <900000>; + + /delete-node/ opp-300000000; + /delete-node/ opp-400000000; + /delete-node/ opp-500000000; + /delete-node/ opp-600000000; + /delete-node/ opp-700000000; + /delete-node/ opp-800000000; + + opp-700000000 { + opp-hz = /bits/ 64 <700000000>; + opp-microvolt = <900000 900000 1000000>; + }; + opp-800000000 { + opp-hz = /bits/ 64 <800000000>; + opp-microvolt = <900000 900000 1000000>; + opp-microvolt-L0 = <962500 962500 1000000>; + opp-microvolt-L1 = <950000 950000 1000000>; + opp-microvolt-L2 = <937500 937500 1000000>; + opp-microvolt-L3 = <925000 925000 1000000>; + opp-microvolt-L4 = <912500 912500 1000000>; + opp-microvolt-L5 = <900000 900000 1000000>; + }; +}; + +&otp { + cpu_code1: cpu-code1@52 { + reg = <0x52 0x2>; + }; +}; + +/delete-node/ &gmac1; +/delete-node/ &pcie2x1; From c28467ad76ab4eb8bd2d300c7077059d640874da Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Tue, 7 Jan 2025 11:53:30 +0800 Subject: [PATCH 05/14] arm64: dts: rockchip: add RK3518 evaluation board devicetree Signed-off-by: Sandy Huang Change-Id: Id2d6a2792bd237db868fe1ad0844a65e77d00af3 --- arch/arm64/boot/dts/rockchip/Makefile | 1 + arch/arm64/boot/dts/rockchip/rk3518-evb.dtsi | 644 ++++++++++++++++++ .../dts/rockchip/rk3518-evb1-ddr4-v10.dts | 12 + .../dts/rockchip/rk3518-evb1-ddr4-v10.dtsi | 108 +++ 4 files changed, 765 insertions(+) create mode 100644 arch/arm64/boot/dts/rockchip/rk3518-evb.dtsi create mode 100644 arch/arm64/boot/dts/rockchip/rk3518-evb1-ddr4-v10.dts create mode 100644 arch/arm64/boot/dts/rockchip/rk3518-evb1-ddr4-v10.dtsi diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile index aa1ee930f484..b7c6b253e861 100644 --- a/arch/arm64/boot/dts/rockchip/Makefile +++ b/arch/arm64/boot/dts/rockchip/Makefile @@ -96,6 +96,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire-excavator.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire-excavator-edp-avb.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399pro-rock-pi-n10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3518-evb1-ddr4-v10.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3528-demo1-lp4-v10.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3528-demo4-ddr4-v10.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3528-demo4-ddr4-v10-linux.dtb diff --git a/arch/arm64/boot/dts/rockchip/rk3518-evb.dtsi b/arch/arm64/boot/dts/rockchip/rk3518-evb.dtsi new file mode 100644 index 000000000000..d30773f4dc4f --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3518-evb.dtsi @@ -0,0 +1,644 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3518.dtsi" +#include +#include +#include +#include "rk-stb-ir-keymap.dtsi" + +/ { + acodec_sound: acodec-sound { + status = "okay"; + compatible = "simple-audio-card"; + simple-audio-card,name = "rk3528-acodec"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,cpu { + sound-dai = <&sai2>; + }; + simple-audio-card,codec { + sound-dai = <&acodec>; + }; + }; + + adc_keys: adc-keys { + status = "okay"; + compatible = "adc-keys"; + io-channels = <&saradc 1>; + io-channel-names = "buttons"; + keyup-threshold-microvolt = <1800000>; + poll-interval = <100>; + + vol-up-key { + label = "volume up"; + linux,code = ; + press-threshold-microvolt = <1750>; + }; + }; + + bt_sco: bt-sco { + status = "disabled"; + compatible = "delta,dfbmcs320"; + #sound-dai-cells = <1>; + }; + + bt_sound: bt-sound { + status = "disabled"; + compatible = "simple-audio-card"; + simple-audio-card,format = "dsp_a"; + simple-audio-card,bitclock-inversion; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "rockchip,bt"; + simple-audio-card,cpu { + sound-dai = <&sai0>; + }; + simple-audio-card,codec { + sound-dai = <&bt_sco 1>; + }; + }; + + 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>; + }; + + es7243_sound: es7243-sound { + status = "disabled"; + compatible = "simple-audio-card"; + simple-audio-card,name = "rockchip,es7243"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <256>; + + simple-audio-card,cpu { + sound-dai = <&sai1>; + }; + simple-audio-card,codec { + sound-dai = <&es7243e>; + }; + }; + + hdmi_sound: hdmi-sound { + compatible = "rockchip,hdmi"; + rockchip,mclk-fs = <128>; + rockchip,card-name = "rockchip,hdmi"; + rockchip,cpu = <&sai3>; + rockchip,codec = <&hdmi>; + rockchip,jack-det; + }; + + pdmics: dummy-codec { + status = "disabled"; + compatible = "rockchip,dummy-codec"; + #sound-dai-cells = <0>; + }; + + pdm_mic_array: pdm-mic-array { + status = "disabled"; + compatible = "simple-audio-card"; + simple-audio-card,name = "rockchip,pdm-mic-array"; + simple-audio-card,cpu { + sound-dai = <&pdm>; + }; + simple-audio-card,codec { + sound-dai = <&pdmics>; + }; + }; + + spdif-sound { + status = "okay"; + compatible = "simple-audio-card"; + simple-audio-card,name = "ROCKCHIP,SPDIF"; + simple-audio-card,mclk-fs = <128>; + simple-audio-card,cpu { + sound-dai = <&spdif_8ch>; + }; + simple-audio-card,codec { + sound-dai = <&spdif_out>; + }; + }; + + spdif_out: spdif-out { + status = "okay"; + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + 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-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_host"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio4 RK_PB5 GPIO_ACTIVE_HIGH>; + vin-supply = <&vcc5v0_sys>; + pinctrl-names = "default"; + pinctrl-0 = <&vcc5v0_host_en>; + }; + + vcc5v0_otg: vcc5v0-otg-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_otg"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio4 RK_PC1 GPIO_ACTIVE_HIGH>; + vin-supply = <&vcc5v0_sys>; + pinctrl-names = "default"; + pinctrl-0 = <&vcc5v0_otg_en>; + }; + + /omit-if-no-ref/ + vccio_sd: vccio-sd { + compatible = "regulator-gpio"; + regulator-name = "vccio_sd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>; + vin-supply = <&vcc5v0_sys>; + states = <1800000 0x0 + 3300000 0x1>; + }; + + vdd_cpu: vdd_logic: vdd_gpu: vdd-cpu { + compatible = "pwm-regulator"; + pwms = <&pwm1 0 5000 1>; + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <746000>; + regulator-max-microvolt = <1201000>; + regulator-init-microvolt = <953000>; + regulator-always-on; + regulator-boot-on; + regulator-settling-time-up-us = <250>; + pwm-supply = <&vcc5v0_sys>; + status = "okay"; + }; + + vdd_0v9_s3: vdd-0v9-s3 { + compatible = "regulator-fixed"; + regulator-name = "vdd_0v9_s3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + vin-supply = <&vcc5v0_sys>; + }; + + vdd_1v8_s3: vdd-1v8-s3 { + compatible = "regulator-fixed"; + regulator-name = "vdd_1v8_s3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vcc5v0_sys>; + }; + + vcc_3v3_s3: vcc-3v3-s3 { + compatible = "regulator-fixed"; + regulator-name = "vcc_3v3_s3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc5v0_sys>; + }; + + /omit-if-no-ref/ + vcc_sd: vcc-sd { + compatible = "regulator-fixed"; + gpio = <&gpio4 RK_PA1 GPIO_ACTIVE_LOW>; + regulator-name = "vcc_sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_3v3_s3>; + }; + + vcc_ddr_s3: vcc-ddr-s3 { + compatible = "regulator-fixed"; + regulator-name = "vcc_ddr_s3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + vin-supply = <&vcc5v0_sys>; + }; +}; + +&acodec { + pa-ctl-gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&avsd { + status = "okay"; +}; + +&combphy_pu { + status = "okay"; +}; + +&cpu0 { + cpu-supply = <&vdd_cpu>; +}; + +&crypto { + status = "okay"; +}; + +&dfi { + status = "okay"; +}; + +&display_subsystem { + status = "okay"; +}; + +&dmc { + center-supply = <&vdd_logic>; + status = "okay"; +}; + +&gmac0 { + status = "okay"; +}; + +&gpu { + mali-supply = <&vdd_gpu>; + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&hdmi_in_vp0 { + status = "okay"; +}; + +&hdmiphy { + status = "okay"; +}; + +&i2c6 { + status = "disabled"; + es7243e: es7243e@10 { + status = "okay"; + #sound-dai-cells = <0>; + compatible = "ES7243E_MicArray_0"; + reg = <0x10>; + }; + + es7243e_11: es7243e@11 { + status = "okay"; + #sound-dai-cells = <0>; + compatible = "ES7243E_MicArray_1"; + reg = <0x11>; + }; + + es7243e_12: es7243e@12 { + status = "okay"; + #sound-dai-cells = <0>; + compatible = "ES7243E_MicArray_2"; + reg = <0x12>; + }; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + +&jpegd { + status = "okay"; +}; + +&jpegd_mmu { + status = "okay"; +}; + +&mpp_srv { + status = "okay"; +}; + +&pinctrl { + usb { + vcc5v0_host_en: vcc5v0-host-en { + rockchip,pins = <4 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + vcc5v0_otg_en: vcc5v0-otg-en { + rockchip,pins = <4 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&pwm0 { + status = "okay"; +}; + +&pwm1 { + status = "okay"; +}; + +&pwm2 { + status = "okay"; +}; + +&pwm3 { + compatible = "rockchip,remotectl-pwm"; + pinctrl-names = "default"; + pinctrl-0 = <&pwm3m0_pins>; + remote_pwm_id = <3>; + handle_cpu_id = <1>; + remote_support_psci = <0>; + status = "okay"; + + ir_key1 { + rockchip,usercode = <0x4040>; + rockchip,key_table = + <0xf2 KEY_REPLY>, + <0xba KEY_BACK>, + <0xf4 KEY_UP>, + <0xf1 KEY_DOWN>, + <0xef KEY_LEFT>, + <0xee KEY_RIGHT>, + <0xbd KEY_HOME>, + <0xea KEY_VOLUMEUP>, + <0xe3 KEY_VOLUMEDOWN>, + <0xe2 KEY_SEARCH>, + <0xb2 KEY_POWER>, + <0xbc KEY_MUTE>, + <0xec KEY_MENU>, + <0xbf 0x190>, + <0xe0 0x191>, + <0xe1 0x192>, + <0xe9 183>, + <0xe6 248>, + <0xe8 185>, + <0xe7 186>, + <0xf0 388>, + <0xbe 0x175>; + }; + + ir_key2 { + rockchip,usercode = <0xff00>; + rockchip,key_table = + <0xf9 KEY_HOME>, + <0xbf KEY_BACK>, + <0xfb KEY_MENU>, + <0xaa KEY_REPLY>, + <0xb9 KEY_UP>, + <0xe9 KEY_DOWN>, + <0xb8 KEY_LEFT>, + <0xea KEY_RIGHT>, + <0xeb KEY_VOLUMEDOWN>, + <0xef KEY_VOLUMEUP>, + <0xf7 KEY_MUTE>, + <0xe7 KEY_POWER>, + <0xfc KEY_POWER>, + <0xa9 KEY_VOLUMEDOWN>, + <0xa8 KEY_PLAYPAUSE>, + <0xe0 KEY_VOLUMEDOWN>, + <0xa5 KEY_VOLUMEDOWN>, + <0xab 183>, + <0xb7 388>, + <0xe8 388>, + <0xf8 184>, + <0xaf 185>, + <0xed KEY_VOLUMEDOWN>, + <0xee 186>, + <0xb3 KEY_VOLUMEDOWN>, + <0xf1 KEY_VOLUMEDOWN>, + <0xf2 KEY_VOLUMEDOWN>, + <0xf3 KEY_SEARCH>, + <0xb4 KEY_VOLUMEDOWN>, + <0xa4 KEY_SETUP>, + <0xbe KEY_SEARCH>; + }; + + ir_key3 { + rockchip,usercode = <0x1dcc>; + rockchip,key_table = + <0xee KEY_REPLY>, + <0xf0 KEY_BACK>, + <0xf8 KEY_UP>, + <0xbb KEY_DOWN>, + <0xef KEY_LEFT>, + <0xed KEY_RIGHT>, + <0xfc KEY_HOME>, + <0xf1 KEY_VOLUMEUP>, + <0xfd KEY_VOLUMEDOWN>, + <0xb7 KEY_SEARCH>, + <0xff KEY_POWER>, + <0xf3 KEY_MUTE>, + <0xbf KEY_MENU>, + <0xf9 0x191>, + <0xf5 0x192>, + <0xb3 388>, + <0xbe KEY_1>, + <0xba KEY_2>, + <0xb2 KEY_3>, + <0xbd KEY_4>, + <0xf9 KEY_5>, + <0xb1 KEY_6>, + <0xfc KEY_7>, + <0xf8 KEY_8>, + <0xb0 KEY_9>, + <0xb6 KEY_0>, + <0xb5 KEY_BACKSPACE>; + }; +}; + +&rga2 { + status = "okay"; +}; + +&rga2_mmu { + status = "okay"; +}; + +&rkvdec { + status = "okay"; +}; + +&rkvdec_mmu { + status = "okay"; +}; + +&rkvenc { + status = "okay"; +}; + +&rkvenc_mmu { + status = "okay"; +}; + +&rkvtunnel { + status = "okay"; +}; + +&rockchip_suspend { + status = "okay"; + rockchip,sleep-debug-en = <1>; + rockchip,virtual-poweroff = <1>; + rockchip,sleep-mode-config = < + (0 + | RKPM_SLP_ARMPD + ) + >; + rockchip,wakeup-config = < + (0 + | RKPM_CPU0_WKUP_EN + | RKPM_GPIO_WKUP_EN + ) + >; + rockchip,pwm-regulator-config = < + (0 + | RKPM_PWM0_M0_REGULATOR_EN + | RKPM_PWM1_M0_REGULATOR_EN + ) + >; +}; + +&sai0 { + pinctrl-0 = <&i2s0m1_lrck &i2s0m1_sclk &i2s0m1_sdi &i2s0m1_sdo>; + status = "disabled"; +}; + +&sai2 { + status = "okay"; +}; + +&sai3 { + status = "okay"; +}; + +&saradc { + status = "okay"; + vref-supply = <&vdd_1v8_s3>; +}; + +&sdhci { + bus-width = <8>; + no-sd; + no-sdio; + non-removable; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + max-frequency = <200000000>; + status = "okay"; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + max-frequency = <150000000>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_det &sdmmc_bus4>; + rockchip,default-sample-phase = <90>; + supports-sd; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + vqmmc-supply = <&vccio_sd>; + vmmc-supply = <&vcc_sd>; + status = "disabled"; +}; + +&sfc { + status = "okay"; +}; + +&spdif_8ch { + status = "okay"; +}; + +&tsadc { + status = "okay"; +}; + +&tve { + status = "okay"; +}; + +&tve_in_vp1 { + status = "okay"; +}; + +&u2phy_host { + phy-supply = <&vcc5v0_host>; + status = "okay"; +}; + +&u2phy_otg { + vbus-supply = <&vcc5v0_otg>; + status = "okay"; +}; + +&usb2phy { + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usbdrd30 { + status = "okay"; +}; + +&usbdrd_dwc3 { + dr_mode = "otg"; + extcon = <&usb2phy>; + status = "okay"; +}; + +&vdpp { + status = "okay"; +}; + +&vdpu { + status = "okay"; +}; + +&vdpu_mmu { + status = "okay"; +}; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3518-evb1-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3518-evb1-ddr4-v10.dts new file mode 100644 index 000000000000..40da44b3e060 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3518-evb1-ddr4-v10.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3518-evb1-ddr4-v10.dtsi" +#include "rk3528-android.dtsi" + +&sdmmc { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3518-evb1-ddr4-v10.dtsi b/arch/arm64/boot/dts/rockchip/rk3518-evb1-ddr4-v10.dtsi new file mode 100644 index 000000000000..f6c8ce23f043 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3518-evb1-ddr4-v10.dtsi @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * + */ + +/dts-v1/; + +#include "rk3518.dtsi" +#include "rk3518-evb.dtsi" + +/ { + model = "Rockchip RK3518 EVB1 DDR4 V10 Board"; + compatible = "rockchip,rk3518-evb1-ddr4-v10", "rockchip,rk3518"; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_enable_h &clkm1_32k_out>; + post-power-on-delay-ms = <200>; + reset-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_LOW>; + status = "okay"; + }; + + wireless_bluetooth: wireless-bluetooth { + compatible = "bluetooth-platdata"; + //wifi-bt-power-toggle; + uart_rts_gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>; + pinctrl-names = "default", "rts_gpio"; + pinctrl-0 = <&uart2m1_rtsn>; + pinctrl-1 = <&uart2m1_gpios>; + BT,reset_gpio = <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>; + BT,wake_gpio = <&gpio1 RK_PB4 GPIO_ACTIVE_HIGH>; + BT,wake_host_irq = <&gpio1 RK_PC2 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + wireless_wlan: wireless-wlan { + compatible = "wlan-platdata"; + rockchip,grf = <&grf>; + wifi_chip_type = "ap6275s"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_host_wake_irq>; + WIFI,host_wake_irq = <&gpio1 RK_PA7 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + +}; + +&es7243_sound { + status = "disabled"; +}; + +&i2c6 { + status = "okay"; +}; + +&sai1 { + status = "okay"; +}; + +&sdio0 { + max-frequency = <200000000>; + no-sd; + no-mmc; + supports-sdio; + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + cap-sdio-irq; + keep-power-in-suspend; + non-removable; + mmc-pwrseq = <&sdio_pwrseq>; + pinctrl-names = "default"; + pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>; + sd-uhs-sdr104; + status = "okay"; +}; + +&uart2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&uart2m1_xfer &uart2m1_ctsn>; +}; + +&pinctrl { + sdio-pwrseq { + wifi_enable_h: wifi-enable-h { + rockchip,pins = <1 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + wifi_32k: wifi-32k { + rockchip,pins = <1 RK_PC3 1 &pcfg_pull_none>; + }; + }; + + wireless-wlan { + wifi_host_wake_irq: wifi-host-wake-irq { + rockchip,pins = <1 RK_PA7 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; + + wireless-bluetooth { + uart2m1_gpios: uart2m1-gpios { + rockchip,pins = <1 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; From 2f6100f677ec24a8ddf1676c45993b5e6e89acd3 Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Mon, 17 Feb 2025 18:34:41 +0800 Subject: [PATCH 06/14] ARM: dts: rockchip: add RK3518 evaluation board devicetree Signed-off-by: Sandy Huang Change-Id: Ib4a8a05d13fc4c2cb3732bbc9365d9c292788e3d --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/rk3518-evb1-ddr4-v10.dts | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 arch/arm/boot/dts/rk3518-evb1-ddr4-v10.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index bd25db8550be..2d98916747c1 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -1239,6 +1239,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \ rk3506g-iotest-v10.dtb \ rk3506g-iotest-v10-pdm.dtb \ rk3506g-test1-v10-audio.dtb \ + rk3518-evb1-ddr4-v10.dtb \ rk3528-demo4-ddr4-v10.dtb \ rk3528-evb1-ddr4-v10.dtb \ rk3528-evb2-ddr3-v10.dtb \ diff --git a/arch/arm/boot/dts/rk3518-evb1-ddr4-v10.dts b/arch/arm/boot/dts/rk3518-evb1-ddr4-v10.dts new file mode 100644 index 000000000000..cb4fcce567e1 --- /dev/null +++ b/arch/arm/boot/dts/rk3518-evb1-ddr4-v10.dts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * + */ + +#include "arm64/rockchip/rk3518-evb1-ddr4-v10.dts" From de85a5fecf84ccd4a2463f16cb9118ca30ecb38b Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Mon, 24 Feb 2025 20:16:31 +0800 Subject: [PATCH 07/14] drm/rockchip: logo: check whether the bridge supports atomic mode before mode fixup According to the include/drm/drm_bridge.h, the following functions are mandatory in atomic mode: &drm_bridge_funcs.atomic_reset() &drm_bridge_funcs.atomic_duplicate_state() &drm_bridge_funcs.atomic_destroy_state() For some bridge drivers that have not supported atomic mode yet: drivers/gpu/drm/bridge/sii902x.c drivers/gpu/drm/bridge/rk630-tve.c ...... The drm_atomic_get_bridge_state() should not be called to get the bridge state by the global atomic state. Without this patch, the null pointer exception will occur. Fixes: 3558926745c8 ("drm/rockchip: logo: call drm_atomic_bridge_chain_check() bridge in mode fixup") Change-Id: I68c953db21a95bf5454fc47c65958dee9d13a8ce Signed-off-by: Damon Ding --- drivers/gpu/drm/rockchip/rockchip_drm_logo.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_logo.c b/drivers/gpu/drm/rockchip/rockchip_drm_logo.c index c113430ea3a2..b39036d9b3fd 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_logo.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_logo.c @@ -739,7 +739,19 @@ static void rockchip_drm_mode_fixup(struct drm_crtc_state *crtc_state, return; bridge = drm_bridge_chain_get_first_bridge(encoder); - if (bridge) { + /* + * Check whether the bridge supports atomic mode or not. + * According to the include/drm/drm_bridge.h, the following functions + * are mandatory in atomic mode: + * &drm_bridge_funcs.atomic_reset() + * &drm_bridge_funcs.atomic_duplicate_state() + * &drm_bridge_funcs.atomic_destroy_state() + * + * For some bridge drivers that have not supported atomic mode yet: + * drivers/gpu/drm/bridge/sii902x.c + * drivers/gpu/drm/bridge/rk630-tve.c + */ + if (bridge && bridge->funcs->atomic_duplicate_state) { bridge_state = drm_atomic_get_bridge_state(crtc_state->state, bridge); if (IS_ERR(bridge_state)) return; From 2367b261cfbbb7b97626e6cdc7b3d4aef4e5b40f Mon Sep 17 00:00:00 2001 From: "chaoyi.chen" Date: Tue, 18 Feb 2025 11:40:43 +0800 Subject: [PATCH 08/14] drm/rockchip: dw_dp: Set output_type in dw_dp_encoder_mode_valid() The output_type may changed after DP encoder bound to CRTC. For example, when performing modeset, calling vop2_crtc_atomic_disable() will clear the output_type to zero. When drm_mode_getconnector() is called again, since the encoder has already been bound to the CRTC, the output_type will not be set and will remain zero. It can lead to the vop2_crtc_mode_valid() being unable to retrieve the correct output_type. This patch set output_type in dw_dp_encoder_mode_valid(), regardless of whether encoder is already bound to a CRTC. Change-Id: I1888d4cc44604072bbf0cbe67a0d21fa8303b7b0 Signed-off-by: chaoyi.chen --- drivers/gpu/drm/rockchip/dw-dp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/dw-dp.c b/drivers/gpu/drm/rockchip/dw-dp.c index 5dc687bfcf67..ec74256903a6 100644 --- a/drivers/gpu/drm/rockchip/dw-dp.c +++ b/drivers/gpu/drm/rockchip/dw-dp.c @@ -3060,7 +3060,10 @@ static enum drm_mode_status dw_dp_encoder_mode_valid(struct drm_encoder *encoder struct drm_device *dev = encoder->dev; struct rockchip_crtc_state *s; - if (!crtc) { + if (crtc) { + s = to_rockchip_crtc_state(crtc->state); + s->output_type = DRM_MODE_CONNECTOR_DisplayPort; + } else { drm_for_each_crtc(crtc, dev) { if (!drm_encoder_crtc_ok(encoder, crtc)) continue; From 1e5e146f5883d248796c68626518dab9698eedc4 Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Wed, 19 Feb 2025 16:37:13 +0800 Subject: [PATCH 09/14] mali400: mali: Add support to set clk to normal pll for rk3518 Change-Id: I74440a835ef513d2f936b5b7f9a89a82ce15e021 Signed-off-by: Finley Xiao --- drivers/gpu/arm/mali400/mali/platform/rk/rk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/arm/mali400/mali/platform/rk/rk.c b/drivers/gpu/arm/mali400/mali/platform/rk/rk.c index 7f0e160cb1b4..aecd44d56d77 100644 --- a/drivers/gpu/arm/mali400/mali/platform/rk/rk.c +++ b/drivers/gpu/arm/mali400/mali/platform/rk/rk.c @@ -465,7 +465,7 @@ static int rk_platform_power_on_gpu(struct device *dev) goto fail_to_enable_regulator; } - if (cpu_is_rk3528()) { + if (cpu_is_rk3528() || cpu_is_rk3518()) { #if defined(CONFIG_MALI_DEVFREQ) && defined(CONFIG_HAVE_CLK) struct mali_device *mdev = dev_get_drvdata(dev); @@ -489,7 +489,7 @@ static void rk_platform_power_off_gpu(struct device *dev) struct rk_context *platform = s_rk_context; if (platform->is_powered) { - if (cpu_is_rk3528()) { + if (cpu_is_rk3528() || cpu_is_rk3518()) { #if defined(CONFIG_MALI_DEVFREQ) && defined(CONFIG_HAVE_CLK) struct mali_device *mdev = dev_get_drvdata(dev); From 230936ca04a421f38a8f42f19fe2c4122a1a843d Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Sat, 22 Feb 2025 15:52:53 +0800 Subject: [PATCH 10/14] drm/rockchip: drv: only rk3528/rk3566/rk3568/rk3588 need ovl lock 1. RK3566/RK3568/RK3588 VOP different VPs share one overlay logic, so need use ovl lock to make sure they're mutually exclusive, Except this three platform, other VOP each VP have independent overlay logic. 2. RK3528 need to reset the p2i_en bit when POST_BUF_EMPTY at post_buf_empty_work_event(), the vop2_cfg_done() must exclusive with userspace commit new frame. Signed-off-by: Sandy Huang Change-Id: I84feb978e00a6c5d32266cd3946610e0cc09868b --- drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 + drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 6 ++++-- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 12 ++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 018efb5583f5..0e48f22e9c4a 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -587,6 +587,7 @@ struct rockchip_drm_private { * OVL_LAYER_SEL/OVL_PORT_SEL */ struct mutex ovl_lock; + bool need_ovl_lock; struct rockchip_drm_vcnt vcnt[ROCKCHIP_MAX_CRTC]; struct rockchip_drm_error_event error_event; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index b2a6be1c5385..4aeaff31bb05 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -290,9 +290,11 @@ static void rockchip_drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state * rockchip_dmcfreq_vop_bandwidth_update(&vop_bw_info); - mutex_lock(&prv->ovl_lock); + if (prv->need_ovl_lock) + mutex_lock(&prv->ovl_lock); drm_atomic_helper_commit_planes(dev, old_state, DRM_PLANE_COMMIT_ACTIVE_ONLY); - mutex_unlock(&prv->ovl_lock); + if (prv->need_ovl_lock) + mutex_unlock(&prv->ovl_lock); drm_atomic_helper_fake_vblank(old_state); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 32276e9033c1..6ce9c900672f 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -15317,6 +15317,7 @@ 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_dev = data; + struct rockchip_drm_private *private; struct vop2 *vop2; struct resource *res; size_t alloc_size; @@ -15348,6 +15349,17 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) vop2->data = vop2_data; vop2->drm_dev = drm_dev; vop2->version = vop2_data->version; + private = drm_dev->dev_private; + /* + * 1. RK3566/RK3568/RK3588 VOP different VPs share one overlay logic, + * so need use ovl lock to make sure they're mutually exclusive; + * 2. RK3528 need to reset the p2i_en bit when POST_BUF_EMPTY at + * post_buf_empty_work_event(), the vop2_cfg_done() must exclusive + * with userspace commit new frame. + */ + private->need_ovl_lock = vop2->version == VOP_VERSION_RK3528 || + vop2->version == VOP_VERSION_RK3568 || + vop2->version == VOP_VERSION_RK3588; dev_set_drvdata(dev, vop2); From 5e7f327ab708d73721539a60dd59c5cbf9dcc083 Mon Sep 17 00:00:00 2001 From: Yandong Lin Date: Mon, 24 Feb 2025 20:08:30 +0800 Subject: [PATCH 11/14] video: rockchip: dvbm: Fix typo Change-Id: Id42b53521e7b797353ef7af36764fc1d656f874e Signed-off-by: Yandong Lin --- include/soc/rockchip/rockchip_dvbm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/soc/rockchip/rockchip_dvbm.h b/include/soc/rockchip/rockchip_dvbm.h index 07b000f48212..d153b6bbd57b 100644 --- a/include/soc/rockchip/rockchip_dvbm.h +++ b/include/soc/rockchip/rockchip_dvbm.h @@ -60,7 +60,7 @@ struct dvbm_port { struct dvbm_isp_cfg_t { u32 chan_id; - struct dmabuf *buf; + struct dma_buf *buf; dma_addr_t dma_addr; u32 ybuf_top; u32 ybuf_bot; From a956e5557266d8983533d48bc0317e763e63529a Mon Sep 17 00:00:00 2001 From: Yandong Lin Date: Wed, 26 Feb 2025 16:19:04 +0800 Subject: [PATCH 12/14] media: rockchip: isp: set the dma_buf of wrap buf Change-Id: I1203b4701e9e77a6bede5210f5273a4919b3536a Signed-off-by: Yandong Lin --- drivers/media/platform/rockchip/isp/isp_dvbm.c | 1 + drivers/media/platform/rockchip/isp/isp_rockit.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/platform/rockchip/isp/isp_dvbm.c b/drivers/media/platform/rockchip/isp/isp_dvbm.c index 6cb5569796f3..e4538995f807 100644 --- a/drivers/media/platform/rockchip/isp/isp_dvbm.c +++ b/drivers/media/platform/rockchip/isp/isp_dvbm.c @@ -47,6 +47,7 @@ int rkisp_dvbm_init(struct rkisp_stream *stream) height = stream->out_fmt.height; wrap_line = dev->cap_dev.wrap_line; dvbm_cfg.dma_addr = buf->dma_addr; + dvbm_cfg.buf = buf->dbuf; dvbm_cfg.ybuf_bot = 0; dvbm_cfg.ybuf_top = width * wrap_line; dvbm_cfg.ybuf_lstd = width; diff --git a/drivers/media/platform/rockchip/isp/isp_rockit.c b/drivers/media/platform/rockchip/isp/isp_rockit.c index 8d91c29766a8..6221342f6399 100644 --- a/drivers/media/platform/rockchip/isp/isp_rockit.c +++ b/drivers/media/platform/rockchip/isp/isp_rockit.c @@ -182,6 +182,7 @@ int rkisp_rockit_buf_queue(struct rockit_cfg *input_rockit_cfg) reg = stream->config->mi.cb_base_ad_init; rkisp_write(ispdev, reg, val, false); stream->dummy_buf.dma_addr = isprk_buf->buff_addr; + stream->dummy_buf.dbuf = isprk_buf->dmabuf; v4l2_info(&ispdev->v4l2_dev, "rockit wrap buf:0x%x\n", isprk_buf->buff_addr); } return -EINVAL; From 3f26cdc2d258b6f9c59187e902026474eda0eb57 Mon Sep 17 00:00:00 2001 From: Mingwei Yan Date: Tue, 25 Feb 2025 15:19:32 +0800 Subject: [PATCH 13/14] media: rockchip: vpss: refactor v_1 for rk3576 Signed-off-by: Mingwei Yan Change-Id: Iff2b7f690b9143807cb5135aa518804dbaf29614 --- drivers/media/platform/rockchip/vpss/Kconfig | 8 + drivers/media/platform/rockchip/vpss/Makefile | 4 + drivers/media/platform/rockchip/vpss/common.c | 13 +- drivers/media/platform/rockchip/vpss/common.h | 11 + drivers/media/platform/rockchip/vpss/dev.c | 71 +- drivers/media/platform/rockchip/vpss/dev.h | 23 +- drivers/media/platform/rockchip/vpss/hw.c | 25 +- drivers/media/platform/rockchip/vpss/hw.h | 18 +- drivers/media/platform/rockchip/vpss/procfs.c | 21 +- drivers/media/platform/rockchip/vpss/procfs.h | 6 + .../rockchip/vpss/{regs.h => regs_v1.h} | 0 drivers/media/platform/rockchip/vpss/stream.c | 2751 +--------------- drivers/media/platform/rockchip/vpss/stream.h | 17 +- .../media/platform/rockchip/vpss/stream_v1.c | 2823 +++++++++++++++++ .../media/platform/rockchip/vpss/stream_v1.h | 42 + .../media/platform/rockchip/vpss/version.h | 1 - drivers/media/platform/rockchip/vpss/vpss.c | 77 +- drivers/media/platform/rockchip/vpss/vpss.h | 26 +- .../platform/rockchip/vpss/vpss_offline.c | 2255 +------------ .../platform/rockchip/vpss/vpss_offline.h | 3 +- .../platform/rockchip/vpss/vpss_offline_v1.c | 2271 +++++++++++++ .../platform/rockchip/vpss/vpss_offline_v1.h | 22 + include/uapi/linux/rk-vpss-config.h | 2 + 23 files changed, 5455 insertions(+), 5035 deletions(-) rename drivers/media/platform/rockchip/vpss/{regs.h => regs_v1.h} (100%) create mode 100644 drivers/media/platform/rockchip/vpss/stream_v1.c create mode 100644 drivers/media/platform/rockchip/vpss/stream_v1.h create mode 100644 drivers/media/platform/rockchip/vpss/vpss_offline_v1.c create mode 100644 drivers/media/platform/rockchip/vpss/vpss_offline_v1.h diff --git a/drivers/media/platform/rockchip/vpss/Kconfig b/drivers/media/platform/rockchip/vpss/Kconfig index 282b627eb689..891b5b59fb5e 100644 --- a/drivers/media/platform/rockchip/vpss/Kconfig +++ b/drivers/media/platform/rockchip/vpss/Kconfig @@ -10,3 +10,11 @@ config VIDEO_ROCKCHIP_VPSS default n help Support for VPSS on the rockchip SoC. + +config VIDEO_ROCKCHIP_VPSS_V1 + bool "vpss1 for rk3576" + depends on CPU_RK3576 + depends on VIDEO_ROCKCHIP_VPSS + default y + help + Support for rk3576 diff --git a/drivers/media/platform/rockchip/vpss/Makefile b/drivers/media/platform/rockchip/vpss/Makefile index db2cba72f536..0bf19031f3af 100644 --- a/drivers/media/platform/rockchip/vpss/Makefile +++ b/drivers/media/platform/rockchip/vpss/Makefile @@ -8,3 +8,7 @@ video_rkvpss-objs += hw.o \ stream.o \ procfs.o \ vpss_offline.o + +video_rkvpss-$(CONFIG_VIDEO_ROCKCHIP_VPSS_V1) += \ + stream_v1.o \ + vpss_offline_v1.o diff --git a/drivers/media/platform/rockchip/vpss/common.c b/drivers/media/platform/rockchip/vpss/common.c index 33532b80b1e3..a5c66b8adf2d 100644 --- a/drivers/media/platform/rockchip/vpss/common.c +++ b/drivers/media/platform/rockchip/vpss/common.c @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include - +#include "vpss.h" #include "common.h" +#include "stream.h" #include "dev.h" -#include "regs.h" +#include "vpss_offline.h" +#include "hw.h" +#include "procfs.h" +#include "regs_v1.h" void rkvpss_idx_write(struct rkvpss_device *dev, u32 reg, u32 val, int idx) @@ -105,7 +106,7 @@ void rkvpss_update_regs(struct rkvpss_device *dev, u32 start, u32 end) if (i == RKVPSS_VPSS_ONLINE) { u32 mask = 0; - for (j = 0; j < RKVPSS_OUTPUT_MAX; j++) { + for (j = 0; j < vpss_outchn_max(hw->vpss_ver); j++) { if (!hw->is_ofl_ch[j]) continue; mask |= (RKVPSS_ISP2VPSS_CHN0_SEL(3) << j * 2); diff --git a/drivers/media/platform/rockchip/vpss/common.h b/drivers/media/platform/rockchip/vpss/common.h index 5d64bf0090df..0bad5eb6aa3f 100644 --- a/drivers/media/platform/rockchip/vpss/common.h +++ b/drivers/media/platform/rockchip/vpss/common.h @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include #include "../isp/isp_vpss.h" #include @@ -88,6 +91,14 @@ static inline struct vb2_queue *to_vb2_queue(struct file *file) return &vnode->buf_queue; } +static inline int vpss_outchn_max(int version) +{ + if (version == VPSS_V10) + return 4; + + return 0; +} + extern int rkvpss_debug; extern struct platform_driver rkvpss_plat_drv; extern int rkvpss_cfginfo_num; diff --git a/drivers/media/platform/rockchip/vpss/dev.c b/drivers/media/platform/rockchip/vpss/dev.c index 6a7798d87bc4..b78831c79c3d 100644 --- a/drivers/media/platform/rockchip/vpss/dev.c +++ b/drivers/media/platform/rockchip/vpss/dev.c @@ -1,23 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "vpss.h" +#include "common.h" +#include "stream.h" #include "dev.h" -#include "regs.h" +#include "vpss_offline.h" +#include "hw.h" +#include "procfs.h" +#include "regs_v1.h" + #include "version.h" #define RKVPSS_VERNO_LEN 10 @@ -34,6 +26,14 @@ static char rkvpss_version[RKVPSS_VERNO_LEN]; module_param_string(version, rkvpss_version, RKVPSS_VERNO_LEN, 0444); MODULE_PARM_DESC(version, "version number"); +static unsigned int rkvpss_wrap_line; +module_param_named(wrap_line, rkvpss_wrap_line, uint, 0644); +MODULE_PARM_DESC(wrap_line, "rkvpss wrap line"); + +char rkvpss_regfile[RKVPSS_REGFILE_LEN]; +module_param_string(reg_file, rkvpss_regfile, RKVPSS_REGFILE_LEN, 0644); +MODULE_PARM_DESC(reg_file, "dump reg file"); + int rkvpss_cfginfo_num = 5; static int rkvpss_get_cfginfo_num(const char *val, const struct kernel_param *kp) @@ -72,7 +72,7 @@ void rkvpss_pipeline_default_fmt(struct rkvpss_device *dev) w = dev->vpss_sdev.out_fmt.width; h = dev->vpss_sdev.out_fmt.height; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) rkvpss_stream_default_fmt(dev, i, w, h, V4L2_PIX_FMT_NV12); } @@ -134,7 +134,7 @@ static int rkvpss_create_links(struct rkvpss_device *dev) struct media_entity *source, *sink; struct rkvpss_stream *stream; unsigned int flags = 0; - int ret; + int ret, i; if (!dev->remote_sd) return -EINVAL; @@ -152,33 +152,14 @@ static int rkvpss_create_links(struct rkvpss_device *dev) flags = MEDIA_LNK_FL_ENABLED; source = &dev->vpss_sdev.sd.entity; - stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH0]; - stream->linked = flags; - sink = &stream->vnode.vdev.entity; - ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags); - if (ret < 0) - goto end; - - stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH1]; - stream->linked = flags; - sink = &stream->vnode.vdev.entity; - ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags); - if (ret < 0) - goto end; - - stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH2]; - stream->linked = flags; - sink = &stream->vnode.vdev.entity; - ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags); - if (ret < 0) - goto end; - - stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH3]; - stream->linked = flags; - sink = &stream->vnode.vdev.entity; - ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags); - if (ret < 0) - goto end; + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) { + stream = &stream_vdev->stream[i]; + stream->linked = flags; + sink = &stream->vnode.vdev.entity; + ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags); + if (ret < 0) + goto end; + } end: return ret; diff --git a/drivers/media/platform/rockchip/vpss/dev.h b/drivers/media/platform/rockchip/vpss/dev.h index c75ae36e892f..eaab883d823e 100644 --- a/drivers/media/platform/rockchip/vpss/dev.h +++ b/drivers/media/platform/rockchip/vpss/dev.h @@ -4,12 +4,20 @@ #ifndef _RKVPSS_DEV_H #define _RKVPSS_DEV_H -#include - -#include "hw.h" -#include "procfs.h" -#include "stream.h" -#include "vpss.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define DRIVER_NAME "rkvpss" #define S0_VDEV_NAME DRIVER_NAME "_scale0" @@ -17,6 +25,8 @@ #define S2_VDEV_NAME DRIVER_NAME "_scale2" #define S3_VDEV_NAME DRIVER_NAME "_scale3" +#define RKVPSS_REGFILE_LEN 50 + enum rkvpss_input { INP_INVAL = 0, INP_ISP, @@ -59,6 +69,7 @@ struct rkvpss_device { atomic_t pipe_stream_cnt; spinlock_t cmsc_lock; + spinlock_t idle_lock; struct rkvpss_cmsc_cfg cmsc_cfg; enum rkvpss_ver vpss_ver; diff --git a/drivers/media/platform/rockchip/vpss/hw.c b/drivers/media/platform/rockchip/vpss/hw.c index 9431bf238fb9..5cb2aff63525 100644 --- a/drivers/media/platform/rockchip/vpss/hw.c +++ b/drivers/media/platform/rockchip/vpss/hw.c @@ -1,27 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "vpss.h" #include "common.h" +#include "stream.h" #include "dev.h" +#include "vpss_offline.h" #include "hw.h" -#include "regs.h" +#include "procfs.h" +#include "regs_v1.h" struct irqs_data { const char *name; @@ -895,7 +882,7 @@ static int rkvpss_hw_probe(struct platform_device *pdev) spin_lock_init(&hw_dev->reg_lock); atomic_set(&hw_dev->refcnt, 0); INIT_LIST_HEAD(&hw_dev->list); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) + for (i = 0; i < vpss_outchn_max(hw_dev->vpss_ver); i++) hw_dev->is_ofl_ch[i] = false; hw_dev->is_ofl_cmsc = false; hw_dev->is_single = true; diff --git a/drivers/media/platform/rockchip/vpss/hw.h b/drivers/media/platform/rockchip/vpss/hw.h index 8fb8bff15ad8..100778ab3dc6 100644 --- a/drivers/media/platform/rockchip/vpss/hw.h +++ b/drivers/media/platform/rockchip/vpss/hw.h @@ -4,8 +4,22 @@ #ifndef _RKVPSS_HW_H #define _RKVPSS_HW_H -#include "common.h" -#include "vpss_offline.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define VPSS_MAX_BUS_CLK 4 #define VPSS_MAX_DEV 8 diff --git a/drivers/media/platform/rockchip/vpss/procfs.c b/drivers/media/platform/rockchip/vpss/procfs.c index b4fe978847ab..c82e1515a4ad 100644 --- a/drivers/media/platform/rockchip/vpss/procfs.c +++ b/drivers/media/platform/rockchip/vpss/procfs.c @@ -1,14 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include +#include "vpss.h" +#include "common.h" +#include "stream.h" #include "dev.h" +#include "vpss_offline.h" +#include "hw.h" #include "procfs.h" -#include "regs.h" +#include "regs_v1.h" + #include "version.h" #ifdef CONFIG_PROC_FS @@ -27,7 +28,7 @@ static void show_hw(struct seq_file *p, struct rkvpss_hw_dev *hw) val = rkvpss_hw_read(hw, RKVPSS_VPSS_CTRL); seq_printf(p, "\tmirror:%s(0x%x)\n", (val & 0x10) ? "ON" : "OFF", val); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + for (i = 0; i < vpss_outchn_max(hw->vpss_ver); i++) { seq_printf(p, "\toutput[%d]", i); val = rkvpss_hw_read(hw, RKVPSS_CMSC_CTRL); mask = RKVPSS_CMSC_CHN_EN(i); @@ -90,7 +91,7 @@ static int vpss_show(struct seq_file *p, void *v) vpss_sdev->in_fmt.width, vpss_sdev->in_fmt.height); seq_printf(p, "is_ofl_cmsc:%d\n", hw->is_ofl_cmsc); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + for (i = 0; i < vpss_outchn_max(hw->vpss_ver); i++) { stream = &dev->stream_vdev.stream[i]; if (hw->is_ofl_ch[i] || !stream->streaming) { seq_printf(p, "is_ofl_ch[%d]:%d OFF\n", i, hw->is_ofl_ch[i]); @@ -109,7 +110,7 @@ static int vpss_show(struct seq_file *p, void *v) stream->crop.height, stream->out_fmt.width, stream->out_fmt.height); - seq_printf(p, "\tframe_cnt:%d rate:%dms delay:%dms frameloss:%d buf_cnt:%d\n", + seq_printf(p, "\tsequence:%d rate:%dms delay:%dms frameloss:%d buf_cnt:%d\n", stream->dbg.id, stream->dbg.interval / 1000 / 1000, stream->dbg.delay / 1000 / 1000, @@ -194,7 +195,7 @@ static int offline_vpss_show(struct seq_file *p, void *v) cfginfo->input.height); seq_printf(p, "%-10s\n", "Output"); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + for (i = 0; i < vpss_outchn_max(hw->vpss_ver); i++) { if (!ofl->hw->is_ofl_ch[i] || !cfginfo->output[i].enable) { seq_printf(p, "\tch[%d] OFF is_ofl_ch[%d]:%d output[%d].enable:%d\n", i, i, ofl->hw->is_ofl_ch[i], i, diff --git a/drivers/media/platform/rockchip/vpss/procfs.h b/drivers/media/platform/rockchip/vpss/procfs.h index 5383c101e4ed..584fa715fe66 100644 --- a/drivers/media/platform/rockchip/vpss/procfs.h +++ b/drivers/media/platform/rockchip/vpss/procfs.h @@ -4,6 +4,12 @@ #ifndef _RKVPSS_PROCFS_H #define _RKVPSS_PROCFS_H +#include +#include +#include +#include +#include + #ifdef CONFIG_PROC_FS int rkvpss_proc_init(struct rkvpss_device *dev); void rkvpss_proc_cleanup(struct rkvpss_device *dev); diff --git a/drivers/media/platform/rockchip/vpss/regs.h b/drivers/media/platform/rockchip/vpss/regs_v1.h similarity index 100% rename from drivers/media/platform/rockchip/vpss/regs.h rename to drivers/media/platform/rockchip/vpss/regs_v1.h diff --git a/drivers/media/platform/rockchip/vpss/stream.c b/drivers/media/platform/rockchip/vpss/stream.c index 98cf598f41dd..611b1018a317 100644 --- a/drivers/media/platform/rockchip/vpss/stream.c +++ b/drivers/media/platform/rockchip/vpss/stream.c @@ -1,2758 +1,65 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "vpss.h" +#include "common.h" +#include "stream.h" #include "dev.h" -#include "regs.h" +#include "vpss_offline.h" +#include "hw.h" +#include "procfs.h" +#include "regs_v1.h" -#define STREAM_OUT_REQ_BUFS_MIN 0 +#include "stream_v1.h" -static void rkvpss_frame_end(struct rkvpss_stream *stream); -static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync); -static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync); -static void rkvpss_stream_mf(struct rkvpss_stream *stream); - -static const struct capture_fmt scl0_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV16, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV12, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - .fmt_type = FMT_YUV, - .bpp = { 8 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV61, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV21, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_TILE420, - .fmt_type = FMT_YUV, - .bpp = { 24 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_TILE422, - .fmt_type = FMT_YUV, - .bpp = { 32 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - } -}; - -static const struct capture_fmt scl1_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV16, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV12, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - .fmt_type = FMT_YUV, - .bpp = { 8 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, - .fmt_type = FMT_RGB, - .bpp = { 16 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = RKVPSS_MI_CHN_WR_RB_SWAP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, - .fmt_type = FMT_RGB, - .bpp = { 24 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = RKVPSS_MI_CHN_WR_RB_SWAP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888, - }, { - .fourcc = V4L2_PIX_FMT_XBGR32, - .fmt_type = FMT_RGB, - .bpp = { 32 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, - .fmt_type = FMT_RGB, - .bpp = { 16 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, - .fmt_type = FMT_RGB, - .bpp = { 24 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888, - }, { - .fourcc = V4L2_PIX_FMT_XRGB32, - .fmt_type = FMT_RGB, - .bpp = { 32 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = RKVPSS_MI_CHN_WR_RB_SWAP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888, - }, { - .fourcc = V4L2_PIX_FMT_NV61, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV21, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_TILE420, - .fmt_type = FMT_YUV, - .bpp = { 24 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_TILE422, - .fmt_type = FMT_YUV, - .bpp = { 32 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - } -}; - -static const struct capture_fmt scl2_3_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV16, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV12, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - .fmt_type = FMT_YUV, - .bpp = { 8 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV61, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV21, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - } -}; - - -static struct stream_config scl0_config = { - .fmts = scl0_fmts, - .fmt_size = ARRAY_SIZE(scl0_fmts), - .frame_end_id = RKVPSS_MI_CHN0_FRM_END, - .crop = { - .ctrl = RKVPSS_CROP1_CTRL, - .update = RKVPSS_CROP1_UPDATE, - .h_offs = RKVPSS_CROP1_0_H_OFFS, - .v_offs = RKVPSS_CROP1_0_V_OFFS, - .h_size = RKVPSS_CROP1_0_H_SIZE, - .v_size = RKVPSS_CROP1_0_V_SIZE, - .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, - .h_offs_shd = RKVPSS_CROP1_0_H_OFFS_SHD, - .v_offs_shd = RKVPSS_CROP1_0_V_OFFS_SHD, - .h_size_shd = RKVPSS_CROP1_0_H_SIZE_SHD, - .v_size_shd = RKVPSS_CROP1_0_V_SIZE_SHD, - }, - .mi = { - .ctrl = RKVPSS_MI_CHN0_WR_CTRL, - .stride = RKVPSS_MI_CHN0_WR_Y_STRIDE, - .y_base = RKVPSS_MI_CHN0_WR_Y_BASE, - .uv_base = RKVPSS_MI_CHN0_WR_CB_BASE, - .y_size = RKVPSS_MI_CHN0_WR_Y_SIZE, - .uv_size = RKVPSS_MI_CHN0_WR_CB_SIZE, - .y_offs_cnt = RKVPSS_MI_CHN0_WR_Y_OFFS_CNT, - .uv_offs_cnt = RKVPSS_MI_CHN0_WR_CB_OFFS_CNT, - .y_pic_width = RKVPSS_MI_CHN0_WR_Y_PIC_WIDTH, - .y_pic_size = RKVPSS_MI_CHN0_WR_Y_PIC_SIZE, - - .ctrl_shd = RKVPSS_MI_CHN0_WR_CTRL_SHD, - .y_shd = RKVPSS_MI_CHN0_WR_Y_BASE_SHD, - .uv_shd = RKVPSS_MI_CHN0_WR_CB_BASE_SHD, - }, -}; - -static struct stream_config scl1_config = { - .fmts = scl1_fmts, - .fmt_size = ARRAY_SIZE(scl1_fmts), - .frame_end_id = RKVPSS_MI_CHN1_FRM_END, - .crop = { - .ctrl = RKVPSS_CROP1_CTRL, - .update = RKVPSS_CROP1_UPDATE, - .h_offs = RKVPSS_CROP1_1_H_OFFS, - .v_offs = RKVPSS_CROP1_1_V_OFFS, - .h_size = RKVPSS_CROP1_1_H_SIZE, - .v_size = RKVPSS_CROP1_1_V_SIZE, - .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, - .h_offs_shd = RKVPSS_CROP1_1_H_OFFS_SHD, - .v_offs_shd = RKVPSS_CROP1_1_V_OFFS_SHD, - .h_size_shd = RKVPSS_CROP1_1_H_SIZE_SHD, - .v_size_shd = RKVPSS_CROP1_1_V_SIZE_SHD, - }, - .scale = { - .ctrl = RKVPSS_SCALE1_CTRL, - .update = RKVPSS_SCALE1_UPDATE, - .src_size = RKVPSS_SCALE1_SRC_SIZE, - .dst_size = RKVPSS_SCALE1_DST_SIZE, - .hy_fac = RKVPSS_SCALE1_HY_FAC, - .hc_fac = RKVPSS_SCALE1_HC_FAC, - .vy_fac = RKVPSS_SCALE1_VY_FAC, - .vc_fac = RKVPSS_SCALE1_VC_FAC, - .hy_offs = RKVPSS_SCALE1_HY_OFFS, - .hc_offs = RKVPSS_SCALE1_HC_OFFS, - .vy_offs = RKVPSS_SCALE1_VY_OFFS, - .vc_offs = RKVPSS_SCALE1_VC_OFFS, - .hy_size = RKVPSS_SCALE1_HY_SIZE, - .hc_size = RKVPSS_SCALE1_HC_SIZE, - .hy_offs_mi = RKVPSS_SCALE1_HY_OFFS_MI, - .hc_offs_mi = RKVPSS_SCALE1_HC_OFFS_MI, - .in_crop_offs = RKVPSS_SCALE1_IN_CROP_OFFSET, - .ctrl_shd = RKVPSS_SCALE1_CTRL_SHD, - .src_size_shd = RKVPSS_SCALE1_SRC_SIZE_SHD, - .dst_size_shd = RKVPSS_SCALE1_DST_SIZE_SHD, - .hy_fac_shd = RKVPSS_SCALE1_HY_FAC_SHD, - .hc_fac_shd = RKVPSS_SCALE1_HC_FAC_SHD, - .vy_fac_shd = RKVPSS_SCALE1_VY_FAC_SHD, - .vc_fac_shd = RKVPSS_SCALE1_VC_FAC_SHD, - .hy_offs_shd = RKVPSS_SCALE1_HY_OFFS_SHD, - .hc_offs_shd = RKVPSS_SCALE1_HC_OFFS_SHD, - .vy_offs_shd = RKVPSS_SCALE1_VY_OFFS_SHD, - .vc_offs_shd = RKVPSS_SCALE1_VC_OFFS_SHD, - .hy_size_shd = RKVPSS_SCALE1_HY_SIZE_SHD, - .hc_size_shd = RKVPSS_SCALE1_HC_SIZE_SHD, - .hy_offs_mi_shd = RKVPSS_SCALE1_HY_OFFS_MI_SHD, - .hc_offs_mi_shd = RKVPSS_SCALE1_HC_OFFS_MI_SHD, - .in_crop_offs_shd = RKVPSS_SCALE1_IN_CROP_OFFSET_SHD, - }, - .mi = { - .ctrl = RKVPSS_MI_CHN1_WR_CTRL, - .stride = RKVPSS_MI_CHN1_WR_Y_STRIDE, - .y_base = RKVPSS_MI_CHN1_WR_Y_BASE, - .uv_base = RKVPSS_MI_CHN1_WR_CB_BASE, - .y_size = RKVPSS_MI_CHN1_WR_Y_SIZE, - .uv_size = RKVPSS_MI_CHN1_WR_CB_SIZE, - .y_offs_cnt = RKVPSS_MI_CHN1_WR_Y_OFFS_CNT, - .uv_offs_cnt = RKVPSS_MI_CHN1_WR_CB_OFFS_CNT, - .y_pic_width = RKVPSS_MI_CHN1_WR_Y_PIC_WIDTH, - .y_pic_size = RKVPSS_MI_CHN1_WR_Y_PIC_SIZE, - - .ctrl_shd = RKVPSS_MI_CHN1_WR_CTRL_SHD, - .y_shd = RKVPSS_MI_CHN1_WR_Y_BASE_SHD, - .uv_shd = RKVPSS_MI_CHN1_WR_CB_BASE_SHD, - }, -}; - -static struct stream_config scl2_config = { - .fmts = scl2_3_fmts, - .fmt_size = ARRAY_SIZE(scl2_3_fmts), - .frame_end_id = RKVPSS_MI_CHN2_FRM_END, - .crop = { - .ctrl = RKVPSS_CROP1_CTRL, - .update = RKVPSS_CROP1_UPDATE, - .h_offs = RKVPSS_CROP1_2_H_OFFS, - .v_offs = RKVPSS_CROP1_2_V_OFFS, - .h_size = RKVPSS_CROP1_2_H_SIZE, - .v_size = RKVPSS_CROP1_2_V_SIZE, - .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, - .h_offs_shd = RKVPSS_CROP1_2_H_OFFS_SHD, - .v_offs_shd = RKVPSS_CROP1_2_V_OFFS_SHD, - .h_size_shd = RKVPSS_CROP1_2_H_SIZE_SHD, - .v_size_shd = RKVPSS_CROP1_2_V_SIZE_SHD, - }, - .scale = { - .ctrl = RKVPSS_SCALE2_CTRL, - .update = RKVPSS_SCALE2_UPDATE, - .src_size = RKVPSS_SCALE2_SRC_SIZE, - .dst_size = RKVPSS_SCALE2_DST_SIZE, - .hy_fac = RKVPSS_SCALE2_HY_FAC, - .hc_fac = RKVPSS_SCALE2_HC_FAC, - .vy_fac = RKVPSS_SCALE2_VY_FAC, - .vc_fac = RKVPSS_SCALE2_VC_FAC, - .hy_offs = RKVPSS_SCALE2_HY_OFFS, - .hc_offs = RKVPSS_SCALE2_HC_OFFS, - .vy_offs = RKVPSS_SCALE2_VY_OFFS, - .vc_offs = RKVPSS_SCALE2_VC_OFFS, - .hy_size = RKVPSS_SCALE2_HY_SIZE, - .hc_size = RKVPSS_SCALE2_HC_SIZE, - .hy_offs_mi = RKVPSS_SCALE2_HY_OFFS_MI, - .hc_offs_mi = RKVPSS_SCALE2_HC_OFFS_MI, - .in_crop_offs = RKVPSS_SCALE2_IN_CROP_OFFSET, - .ctrl_shd = RKVPSS_SCALE2_CTRL_SHD, - .src_size_shd = RKVPSS_SCALE2_SRC_SIZE_SHD, - .dst_size_shd = RKVPSS_SCALE2_DST_SIZE_SHD, - .hy_fac_shd = RKVPSS_SCALE2_HY_FAC_SHD, - .hc_fac_shd = RKVPSS_SCALE2_HC_FAC_SHD, - .vy_fac_shd = RKVPSS_SCALE2_VY_FAC_SHD, - .vc_fac_shd = RKVPSS_SCALE2_VC_FAC_SHD, - .hy_offs_shd = RKVPSS_SCALE2_HY_OFFS_SHD, - .hc_offs_shd = RKVPSS_SCALE2_HC_OFFS_SHD, - .vy_offs_shd = RKVPSS_SCALE2_VY_OFFS_SHD, - .vc_offs_shd = RKVPSS_SCALE2_VC_OFFS_SHD, - .hy_size_shd = RKVPSS_SCALE2_HY_SIZE_SHD, - .hc_size_shd = RKVPSS_SCALE2_HC_SIZE_SHD, - .hy_offs_mi_shd = RKVPSS_SCALE2_HY_OFFS_MI_SHD, - .hc_offs_mi_shd = RKVPSS_SCALE2_HC_OFFS_MI_SHD, - .in_crop_offs_shd = RKVPSS_SCALE2_IN_CROP_OFFSET_SHD, - }, - .mi = { - .ctrl = RKVPSS_MI_CHN2_WR_CTRL, - .stride = RKVPSS_MI_CHN2_WR_Y_STRIDE, - .y_base = RKVPSS_MI_CHN2_WR_Y_BASE, - .uv_base = RKVPSS_MI_CHN2_WR_CB_BASE, - .y_size = RKVPSS_MI_CHN2_WR_Y_SIZE, - .uv_size = RKVPSS_MI_CHN2_WR_CB_SIZE, - .y_offs_cnt = RKVPSS_MI_CHN2_WR_Y_OFFS_CNT, - .uv_offs_cnt = RKVPSS_MI_CHN2_WR_CB_OFFS_CNT, - .y_pic_width = RKVPSS_MI_CHN2_WR_Y_PIC_WIDTH, - .y_pic_size = RKVPSS_MI_CHN2_WR_Y_PIC_SIZE, - - .ctrl_shd = RKVPSS_MI_CHN2_WR_CTRL_SHD, - .y_shd = RKVPSS_MI_CHN2_WR_Y_BASE_SHD, - .uv_shd = RKVPSS_MI_CHN2_WR_CB_BASE_SHD, - }, -}; - -static struct stream_config scl3_config = { - .fmts = scl2_3_fmts, - .fmt_size = ARRAY_SIZE(scl2_3_fmts), - .frame_end_id = RKVPSS_MI_CHN3_FRM_END, - .crop = { - .ctrl = RKVPSS_CROP1_CTRL, - .update = RKVPSS_CROP1_UPDATE, - .h_offs = RKVPSS_CROP1_3_H_OFFS, - .v_offs = RKVPSS_CROP1_3_V_OFFS, - .h_size = RKVPSS_CROP1_3_H_SIZE, - .v_size = RKVPSS_CROP1_3_V_SIZE, - .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, - .h_offs_shd = RKVPSS_CROP1_3_H_OFFS_SHD, - .v_offs_shd = RKVPSS_CROP1_3_V_OFFS_SHD, - .h_size_shd = RKVPSS_CROP1_3_H_SIZE_SHD, - .v_size_shd = RKVPSS_CROP1_3_V_SIZE_SHD, - }, - .scale = { - .ctrl = RKVPSS_SCALE3_CTRL, - .update = RKVPSS_SCALE3_UPDATE, - .src_size = RKVPSS_SCALE3_SRC_SIZE, - .dst_size = RKVPSS_SCALE3_DST_SIZE, - .hy_fac = RKVPSS_SCALE3_HY_FAC, - .hc_fac = RKVPSS_SCALE3_HC_FAC, - .vy_fac = RKVPSS_SCALE3_VY_FAC, - .vc_fac = RKVPSS_SCALE3_VC_FAC, - .hy_offs = RKVPSS_SCALE3_HY_OFFS, - .hc_offs = RKVPSS_SCALE3_HC_OFFS, - .vy_offs = RKVPSS_SCALE3_VY_OFFS, - .vc_offs = RKVPSS_SCALE3_VC_OFFS, - .hy_size = RKVPSS_SCALE3_HY_SIZE, - .hc_size = RKVPSS_SCALE3_HC_SIZE, - .hy_offs_mi = RKVPSS_SCALE3_HY_OFFS_MI, - .hc_offs_mi = RKVPSS_SCALE3_HC_OFFS_MI, - .in_crop_offs = RKVPSS_SCALE3_IN_CROP_OFFSET, - .ctrl_shd = RKVPSS_SCALE3_CTRL_SHD, - .src_size_shd = RKVPSS_SCALE3_SRC_SIZE_SHD, - .dst_size_shd = RKVPSS_SCALE3_DST_SIZE_SHD, - .hy_fac_shd = RKVPSS_SCALE3_HY_FAC_SHD, - .hc_fac_shd = RKVPSS_SCALE3_HC_FAC_SHD, - .vy_fac_shd = RKVPSS_SCALE3_VY_FAC_SHD, - .vc_fac_shd = RKVPSS_SCALE3_VC_FAC_SHD, - .hy_offs_shd = RKVPSS_SCALE3_HY_OFFS_SHD, - .hc_offs_shd = RKVPSS_SCALE3_HC_OFFS_SHD, - .vy_offs_shd = RKVPSS_SCALE3_VY_OFFS_SHD, - .vc_offs_shd = RKVPSS_SCALE3_VC_OFFS_SHD, - .hy_size_shd = RKVPSS_SCALE3_HY_SIZE_SHD, - .hc_size_shd = RKVPSS_SCALE3_HC_SIZE_SHD, - .hy_offs_mi_shd = RKVPSS_SCALE3_HY_OFFS_MI_SHD, - .hc_offs_mi_shd = RKVPSS_SCALE3_HC_OFFS_MI_SHD, - .in_crop_offs_shd = RKVPSS_SCALE3_IN_CROP_OFFSET_SHD, - }, - .mi = { - .ctrl = RKVPSS_MI_CHN3_WR_CTRL, - .stride = RKVPSS_MI_CHN3_WR_Y_STRIDE, - .y_base = RKVPSS_MI_CHN3_WR_Y_BASE, - .uv_base = RKVPSS_MI_CHN3_WR_CB_BASE, - .y_size = RKVPSS_MI_CHN3_WR_Y_SIZE, - .uv_size = RKVPSS_MI_CHN3_WR_CB_SIZE, - .y_offs_cnt = RKVPSS_MI_CHN3_WR_Y_OFFS_CNT, - .uv_offs_cnt = RKVPSS_MI_CHN3_WR_CB_OFFS_CNT, - .y_pic_width = RKVPSS_MI_CHN3_WR_Y_PIC_WIDTH, - .y_pic_size = RKVPSS_MI_CHN3_WR_Y_PIC_SIZE, - - .ctrl_shd = RKVPSS_MI_CHN3_WR_CTRL_SHD, - .y_shd = RKVPSS_MI_CHN3_WR_Y_BASE_SHD, - .uv_shd = RKVPSS_MI_CHN3_WR_CB_BASE_SHD, - }, -}; - -static void calc_unite_scl_params(struct rkvpss_stream *stream) +void rkvpss_cmsc_config(struct rkvpss_device *dev, bool sync) { - struct rkvpss_online_unite_params *params = &stream->unite_params; - u32 right_scl_need_size_y, right_scl_need_size_c; - u32 left_in_used_size_y, left_in_used_size_c; - u32 right_fst_position_y, right_fst_position_c; - u32 right_y_crop_total; - u32 right_c_crop_total; - - params->y_w_fac = (stream->crop.width - 1) * 4096 / - (stream->out_fmt.width - 1); - params->c_w_fac = (stream->crop.width / 2 - 1) * 4096 / - (stream->out_fmt.width / 2 - 1); - params->y_h_fac = (stream->crop.height - 1) * 4096 / - (stream->out_fmt.height - 1); - params->c_h_fac = (stream->crop.height - 1) * 4096 / - (stream->out_fmt.height - 1); - - right_fst_position_y = stream->out_fmt.width / 2 * - params->y_w_fac; - right_fst_position_c = stream->out_fmt.width / 2 / 2 * - params->c_w_fac; - - left_in_used_size_y = right_fst_position_y >> 12; - left_in_used_size_c = (right_fst_position_c >> 12) * 2; - - params->y_w_phase = right_fst_position_y & 0xfff; - params->c_w_phase = right_fst_position_c & 0xfff; - - right_scl_need_size_y = stream->crop.width - - left_in_used_size_y; - params->right_scl_need_size_y = right_scl_need_size_y; - right_scl_need_size_c = stream->crop.width - - left_in_used_size_c; - params->right_scl_need_size_c = right_scl_need_size_c; - - if (stream->id == 0 && stream->crop.width != stream->out_fmt.width) { - right_y_crop_total = stream->crop.width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL - - right_scl_need_size_y - 3; - right_c_crop_total = stream->crop.width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL - - right_scl_need_size_c - 6; - } else { - right_y_crop_total = stream->crop.width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL - - right_scl_need_size_y; - right_c_crop_total = stream->crop.width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL - - right_scl_need_size_c; - } - - params->quad_crop_w = ALIGN_DOWN(min(right_y_crop_total, right_c_crop_total), 2); - - params->scl_in_crop_w_y = right_y_crop_total - params->quad_crop_w; - params->scl_in_crop_w_c = right_c_crop_total - params->quad_crop_w; - - if (rkvpss_debug >= 2) { - v4l2_info(&stream->dev->v4l2_dev, - "%s ch:%d y_w_fac:%u c_w_fac:%u y_h_fac:%u c_h_fac:%u\n", - __func__, stream->id, params->y_w_fac, - params->c_w_fac, params->y_h_fac, params->c_h_fac); - v4l2_info(&stream->dev->v4l2_dev, - "\t%s y_w_phase:%u c_w_phase:%u quad_crop_w:%u scl_in_crop_w_y:%u scl_in_crop_w_c:%u\n", - __func__, params->y_w_phase, params->c_w_phase, - params->quad_crop_w, - params->scl_in_crop_w_y, params->scl_in_crop_w_c); - v4l2_info(&stream->dev->v4l2_dev, - "\t%s right_scl_need_size_y:%u right_scl_need_size_c:%u\n", - __func__, params->right_scl_need_size_y, - params->right_scl_need_size_c); - } + if (dev->vpss_ver == VPSS_V10) + rkvpss_cmsc_config_v1(dev, sync); } int rkvpss_stream_buf_cnt(struct rkvpss_stream *stream) { - unsigned long lock_flags = 0; - struct rkvpss_buffer *buf, *tmp; - int cnt = 0; - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - list_for_each_entry_safe(buf, tmp, &stream->buf_queue, queue) - cnt++; - if (stream->curr_buf) - cnt++; - if (stream->next_buf && stream->next_buf != stream->curr_buf) - cnt++; - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - return cnt; -} - -static void stream_frame_start(struct rkvpss_stream *stream, u32 irq) -{ - struct rkvpss_device *dev = stream->dev; - - if (stream->is_crop_upd) { - if (dev->unite_mode) - calc_unite_scl_params(stream); - rkvpss_stream_scale(stream, true, !irq); - rkvpss_stream_crop(stream, true, !irq); - } - if (!irq && !stream->curr_buf && - !stream->dev->hw_dev->is_single) - stream->ops->update_mi(stream); -} - -static void scl_force_update(struct rkvpss_stream *stream) -{ - u32 val; - - switch (stream->id) { - case RKVPSS_OUTPUT_CH0: - val = RKVPSS_MI_CHN0_FORCE_UPD; - break; - case RKVPSS_OUTPUT_CH1: - val = RKVPSS_MI_CHN1_FORCE_UPD; - break; - case RKVPSS_OUTPUT_CH2: - val = RKVPSS_MI_CHN2_FORCE_UPD; - break; - case RKVPSS_OUTPUT_CH3: - val = RKVPSS_MI_CHN3_FORCE_UPD; - break; - default: - return; - } - /* need update two for online2 mode */ - rkvpss_unite_write(stream->dev, RKVPSS_MI_WR_INIT, val); - rkvpss_unite_write(stream->dev, RKVPSS_MI_WR_INIT, val); -} - -static void scl_update_mi(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - struct rkvpss_buffer *buf = NULL; - unsigned long lock_flags = 0; - u32 y_base, uv_base; - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - if (!list_empty(&stream->buf_queue) && !stream->curr_buf) { - buf = list_first_entry(&stream->buf_queue, struct rkvpss_buffer, queue); - list_del(&buf->queue); - stream->curr_buf = buf; - } - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - - if (buf) { - y_base = buf->dma[0]; - uv_base = buf->dma[1]; - rkvpss_idx_write(dev, stream->config->mi.y_base, y_base, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, stream->config->mi.uv_base, uv_base, VPSS_UNITE_LEFT); - if (dev->unite_mode) { - y_base = buf->dma[0] + stream->out_fmt.width / 2; - uv_base = buf->dma[1] + stream->out_fmt.width / 2; - rkvpss_idx_write(dev, stream->config->mi.y_base, y_base, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, stream->config->mi.uv_base, uv_base, VPSS_UNITE_RIGHT); - } - - scl_force_update(stream); - if (stream->is_pause) { - stream->is_pause = false; - stream->ops->enable_mi(stream); - } - } else if (!stream->is_pause) { - stream->is_pause = true; - stream->ops->disable_mi(stream); - } - - v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, - "%s id:%d unite_index:%d Y:0x%x UV:0x%x | Y_SHD:0x%x\n", - __func__, stream->id, dev->unite_index, - rkvpss_idx_read(dev, stream->config->mi.y_base, dev->unite_index), - rkvpss_idx_read(dev, stream->config->mi.uv_base, dev->unite_index), - rkvpss_hw_read(dev->hw_dev, stream->config->mi.y_shd)); -} - -static void scl_config_mi(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - struct capture_fmt *fmt = &stream->out_cap_fmt; - struct v4l2_pix_format_mplane *out_fmt = &stream->out_fmt; - u32 reg, val, mask; - - val = out_fmt->plane_fmt[0].bytesperline; - reg = stream->config->mi.stride; - rkvpss_unite_write(dev, reg, val); - rkvpss_unite_write(dev, reg, val); - - switch (fmt->fourcc) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - val = out_fmt->plane_fmt[0].bytesperline / 2; - break; - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_XRGB32: - val = out_fmt->plane_fmt[0].bytesperline / 4; - break; - default: - val = out_fmt->plane_fmt[0].bytesperline; - break; - } - - val = val * out_fmt->height; - reg = stream->config->mi.y_pic_size; - rkvpss_unite_write(dev, reg, val); - - val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height; - reg = stream->config->mi.y_size; - rkvpss_unite_write(dev, reg, val); - - val = out_fmt->plane_fmt[1].sizeimage; - reg = stream->config->mi.uv_size; - rkvpss_unite_write(dev, reg, val); - - reg = stream->config->mi.y_offs_cnt; - rkvpss_unite_write(dev, reg, 0); - reg = stream->config->mi.uv_offs_cnt; - rkvpss_unite_write(dev, reg, 0); - - val = fmt->wr_fmt | fmt->output_fmt | fmt->swap | - RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; - reg = stream->config->mi.ctrl; - rkvpss_unite_write(dev, reg, val); - - switch (fmt->fourcc) { - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_VYUY: - mask = RKVPSS_MI_WR_UV_SWAP; - val = RKVPSS_MI_WR_UV_SWAP; - rkvpss_hw_set_bits(dev->hw_dev, RKVPSS_MI_WR_CTRL, mask, val); - break; - case V4L2_PIX_FMT_TILE420: - case V4L2_PIX_FMT_TILE422: - mask = RKVPSS_MI_WR_TILE_SEL(3); - val = RKVPSS_MI_WR_TILE_SEL(stream->id + 1); - rkvpss_hw_set_bits(dev->hw_dev, RKVPSS_MI_WR_CTRL, mask, val); - break; - default: - break; - } - - stream->is_mf_upd = true; - rkvpss_frame_end(stream); -} - -static void scl_enable_mi(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - u32 val, mask; - - switch (stream->id) { - case RKVPSS_OUTPUT_CH0: - val = RKVPSS_ISP2VPSS_CHN0_SEL(2); - mask = RKVPSS_ISP2VPSS_CHN0_SEL(3); - break; - case RKVPSS_OUTPUT_CH1: - val = RKVPSS_ISP2VPSS_CHN1_SEL(2); - mask = RKVPSS_ISP2VPSS_CHN1_SEL(3); - break; - case RKVPSS_OUTPUT_CH2: - val = RKVPSS_ISP2VPSS_CHN2_SEL(2); - mask = RKVPSS_ISP2VPSS_CHN2_SEL(3); - break; - case RKVPSS_OUTPUT_CH3: - val = RKVPSS_ISP2VPSS_CHN3_SEL(2); - mask = RKVPSS_ISP2VPSS_CHN3_SEL(3); - break; - default: - return; - } - val |= RKVPSS_ISP2VPSS_ONLINE2; - if (!dev->hw_dev->is_ofl_cmsc) - val |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN; - rkvpss_unite_set_bits(dev, RKVPSS_VPSS_ONLINE, mask, val); - val = RKVPSS_ONLINE2_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD; - rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val); -} - -static void scl_disable_mi(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - u32 val; - - switch (stream->id) { - case RKVPSS_OUTPUT_CH0: - val = RKVPSS_ISP2VPSS_CHN0_SEL(3); - break; - case RKVPSS_OUTPUT_CH1: - val = RKVPSS_ISP2VPSS_CHN1_SEL(3); - break; - case RKVPSS_OUTPUT_CH2: - val = RKVPSS_ISP2VPSS_CHN2_SEL(3); - break; - case RKVPSS_OUTPUT_CH3: - val = RKVPSS_ISP2VPSS_CHN3_SEL(3); - break; - default: - return; - } - - rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_ONLINE, val); - val = RKVPSS_ONLINE2_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD; - rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val); -} - -static struct streams_ops scl_stream_ops = { - .config_mi = scl_config_mi, - .enable_mi = scl_enable_mi, - .disable_mi = scl_disable_mi, - .update_mi = scl_update_mi, - .frame_start = stream_frame_start, -}; - -static void rkvpss_buf_done_task(unsigned long arg) -{ - struct rkvpss_stream *stream = (struct rkvpss_stream *)arg; - struct rkvpss_vdev_node *node = &stream->vnode; - struct rkvpss_buffer *buf = NULL; - unsigned long lock_flags = 0; - LIST_HEAD(local_list); - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - list_replace_init(&stream->buf_done_list, &local_list); - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - - while (!list_empty(&local_list)) { - buf = list_first_entry(&local_list, struct rkvpss_buffer, queue); - list_del(&buf->queue); - v4l2_dbg(2, rkvpss_debug, &stream->dev->v4l2_dev, - "%s stream:%d index:%d seq:%d buf:0x%x done\n", - node->vdev.name, stream->id, buf->vb.vb2_buf.index, - buf->vb.sequence, buf->dma[0]); - vb2_buffer_done(&buf->vb.vb2_buf, - stream->streaming ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR); - } -} - -static void rkvpss_stream_buf_done(struct rkvpss_stream *stream, - struct rkvpss_buffer *buf) -{ - unsigned long lock_flags = 0; - - if (!stream || !buf) - return; - - v4l2_dbg(3, rkvpss_debug, &stream->dev->v4l2_dev, - "stream:%d\n", stream->id); - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - list_add_tail(&buf->queue, &stream->buf_done_list); - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - tasklet_schedule(&stream->buf_done_tasklet); -} - -static void rkvpss_fill_frame_info(struct rkvpss_frame_info *dst_info, - struct rkisp_vpss_frame_info *src_info) -{ - dst_info->timestamp = src_info->timestamp; - - dst_info->seq = src_info->seq; - dst_info->hdr = src_info->hdr; - dst_info->rolling_shutter_skew = src_info->rolling_shutter_skew; - - dst_info->sensor_exposure_time = src_info->sensor_exposure_time; - dst_info->sensor_analog_gain = src_info->sensor_analog_gain; - dst_info->sensor_digital_gain = src_info->sensor_digital_gain; - dst_info->isp_digital_gain = src_info->isp_digital_gain; - - dst_info->sensor_exposure_time_m = src_info->sensor_exposure_time_m; - dst_info->sensor_analog_gain_m = src_info->sensor_analog_gain_m; - dst_info->sensor_digital_gain_m = src_info->sensor_digital_gain_m; - dst_info->isp_digital_gain_m = src_info->isp_digital_gain_m; - - dst_info->sensor_exposure_time_l = src_info->sensor_exposure_time_l; - dst_info->sensor_analog_gain_l = src_info->sensor_analog_gain_l; - dst_info->sensor_digital_gain_l = src_info->sensor_digital_gain_l; - dst_info->isp_digital_gain_l = src_info->isp_digital_gain_l; -} - -static void rkvpss_frame_end(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - struct rkvpss_subdev *sdev = &dev->vpss_sdev; - struct rkvpss_buffer *buf = NULL; - unsigned long lock_flags = 0; - - v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, - "stream:%d\n", stream->id); - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - if (stream->curr_buf) { - buf = stream->curr_buf; - stream->curr_buf = NULL; - } - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - - if (buf) { - struct vb2_buffer *vb2_buf = &buf->vb.vb2_buf; - struct capture_fmt *fmt = &stream->out_cap_fmt; - u64 ns = sdev->frame_timestamp; - int i; - - for (i = 0; i < fmt->mplanes; i++) { - u32 payload_size = stream->out_fmt.plane_fmt[i].sizeimage; - - vb2_set_plane_payload(vb2_buf, i, payload_size); - - if (stream->is_attach_info && i == fmt->mplanes - 1) { - struct rkvpss_frame_info *dst_info = buf->vaddr[i] + payload_size; - struct rkisp_vpss_frame_info *src_info = &dev->frame_info; - - rkvpss_fill_frame_info(dst_info, src_info); - } - } - if (!ns) - ns = ktime_get_ns(); - buf->vb.vb2_buf.timestamp = ns; - buf->vb.sequence = sdev->frame_seq; - - ns = ktime_get_ns(); - stream->dbg.frameloss += buf->vb.sequence - stream->dbg.id - 1; - stream->dbg.id = buf->vb.sequence; - stream->dbg.delay = ns - buf->vb.vb2_buf.timestamp; - stream->dbg.interval = ns - stream->dbg.timestamp; - stream->dbg.timestamp = ns; - rkvpss_stream_buf_done(stream, buf); - } - - rkvpss_stream_mf(stream); - stream->ops->update_mi(stream); -} - -static int rkvpss_queue_setup(struct vb2_queue *queue, - unsigned int *num_buffers, - unsigned int *num_planes, - unsigned int sizes[], - struct device *alloc_ctxs[]) -{ - struct rkvpss_stream *stream = queue->drv_priv; - struct rkvpss_device *dev = stream->dev; - const struct v4l2_pix_format_mplane *pixm = NULL; - const struct capture_fmt *cap_fmt = NULL; - u32 i; - - pixm = &stream->out_fmt; - if (!pixm->width || !pixm->height) - return -EINVAL; - cap_fmt = &stream->out_cap_fmt; - *num_planes = cap_fmt->mplanes; - - for (i = 0; i < cap_fmt->mplanes; i++) { - const struct v4l2_plane_pix_format *plane_fmt; - - plane_fmt = &pixm->plane_fmt[i]; - sizes[i] = plane_fmt->sizeimage / pixm->height * ALIGN(pixm->height, 16); - - if (stream->is_attach_info && i == cap_fmt->mplanes - 1) - sizes[i] += sizeof(struct rkvpss_frame_info); - } - - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s stream:%d count %d, size %d\n", - v4l2_type_names[queue->type], - stream->id, *num_buffers, sizes[0]); - - return 0; -} - -static void rkvpss_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct rkvpss_buffer *vpssbuf = to_rkvpss_buffer(vbuf); - struct vb2_queue *queue = vb->vb2_queue; - struct rkvpss_stream *stream = queue->drv_priv; - struct rkvpss_device *dev = stream->dev; - struct v4l2_pix_format_mplane *pixm = &stream->out_fmt; - struct capture_fmt *cap_fmt = &stream->out_cap_fmt; - unsigned long lock_flags = 0; - struct sg_table *sgt; - u32 height, size; - int i; - - for (i = 0; i < cap_fmt->mplanes; i++) { - sgt = vb2_dma_sg_plane_desc(vb, i); - vpssbuf->dma[i] = sg_dma_address(sgt->sgl); - - vpssbuf->vaddr[i] = vb2_plane_vaddr(vb, i); - } - /* - * NOTE: plane_fmt[0].sizeimage is total size of all planes for single - * memory plane formats, so calculate the size explicitly. - */ - if (cap_fmt->mplanes == 1) { - for (i = 0; i < cap_fmt->cplanes - 1; i++) { - height = pixm->height; - size = (i == 0) ? - pixm->plane_fmt[i].bytesperline * height : - pixm->plane_fmt[i].sizeimage; - vpssbuf->dma[i + 1] = vpssbuf->dma[i] + size; - } - } - - v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, - "%s stream:%d index:%d buf:0x%x\n", __func__, - stream->id, vb->index, vpssbuf->dma[0]); - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - list_add_tail(&vpssbuf->queue, &stream->buf_queue); - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - if (dev->hw_dev->is_single && - stream->streaming && !stream->curr_buf) - stream->ops->update_mi(stream); -} - -static void destroy_buf_queue(struct rkvpss_stream *stream, - enum vb2_buffer_state state) -{ - unsigned long lock_flags = 0; - struct rkvpss_buffer *buf; - LIST_HEAD(queue_local_list); - LIST_HEAD(done_local_list); - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - if (stream->curr_buf) { - list_add_tail(&stream->curr_buf->queue, &stream->buf_queue); - stream->curr_buf = NULL; - } - list_replace_init(&stream->buf_queue, &queue_local_list); - list_replace_init(&stream->buf_done_list, &done_local_list); - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - - while (!list_empty(&queue_local_list)) { - buf = list_first_entry(&queue_local_list, struct rkvpss_buffer, queue); - list_del(&buf->queue); - buf->vb.vb2_buf.synced = false; - vb2_buffer_done(&buf->vb.vb2_buf, state); - } - - while (!list_empty(&done_local_list)) { - buf = list_first_entry(&done_local_list, struct rkvpss_buffer, queue); - list_del(&buf->queue); - buf->vb.vb2_buf.synced = false; - vb2_buffer_done(&buf->vb.vb2_buf, state); - } -} - -static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync) -{ - struct rkvpss_device *dev = stream->dev; - struct v4l2_rect *crop = &stream->crop; - u32 reg_ctrl = stream->config->crop.ctrl; - u32 reg_upd = stream->config->crop.update; - u32 reg_h_offs = stream->config->crop.h_offs; - u32 reg_v_offs = stream->config->crop.v_offs; - u32 reg_h_size = stream->config->crop.h_size; - u32 reg_v_size = stream->config->crop.v_size; - u32 val; - - val = RKVPSS_CROP_CHN_EN(stream->id); - if (!dev->unite_mode) { - if (on) { - rkvpss_unite_set_bits(dev, reg_ctrl, 0, val); - rkvpss_unite_write(dev, reg_h_offs, crop->left); - rkvpss_unite_write(dev, reg_v_offs, crop->top); - rkvpss_unite_write(dev, reg_h_size, crop->width); - rkvpss_unite_write(dev, reg_v_size, crop->height); - - v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, - "crop left:%d top:%d w:%d h:%d\n", - crop->left, crop->top, crop->width, crop->height); - } else { - rkvpss_unite_clear_bits(dev, reg_ctrl, val); - } - if (sync) { - val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id); - val |= RKVPSS_CROP_GEN_UPD; - rkvpss_unite_write(dev, reg_upd, val); - rkvpss_unite_write(dev, reg_upd, val); - } - } else { - if (on) { - if (crop->left + crop->width / 2 != dev->vpss_sdev.in_fmt.width / 2) { - v4l2_err(&dev->v4l2_dev, - "unite need centered crop left:%d top:%d w:%d h:%d\n", - crop->left, crop->top, crop->width, crop->height); - return -EINVAL; - } - /* unite left */ - rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, reg_h_offs, crop->left, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, reg_v_offs, crop->top, VPSS_UNITE_LEFT); - /* if no scale left don't enlarge */ - if (crop->width == stream->out_fmt.width) - rkvpss_idx_write(dev, reg_h_size, crop->width / 2, - VPSS_UNITE_LEFT); - else - rkvpss_idx_write(dev, reg_h_size, crop->width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, reg_v_size, crop->height, VPSS_UNITE_LEFT); - v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, - "right crop left:%d top:%d w:%d h:%d\n", - rkvpss_idx_read(dev, reg_h_offs, VPSS_UNITE_LEFT), - rkvpss_idx_read(dev, reg_v_offs, VPSS_UNITE_LEFT), - rkvpss_idx_read(dev, reg_h_size, VPSS_UNITE_LEFT), - rkvpss_idx_read(dev, reg_v_size, VPSS_UNITE_LEFT)); - - /* unite right */ - rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, reg_h_offs, stream->unite_params.quad_crop_w, - VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, reg_v_offs, crop->top, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, reg_h_size, crop->width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL - - stream->unite_params.quad_crop_w, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, reg_v_size, crop->height, VPSS_UNITE_RIGHT); - v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, - "right crop left:%d top:%d w:%d h:%d\n", - rkvpss_idx_read(dev, reg_h_offs, VPSS_UNITE_RIGHT), - rkvpss_idx_read(dev, reg_v_offs, VPSS_UNITE_RIGHT), - rkvpss_idx_read(dev, reg_h_size, VPSS_UNITE_RIGHT), - rkvpss_idx_read(dev, reg_v_size, VPSS_UNITE_RIGHT)); - } else { - rkvpss_idx_clear_bits(dev, reg_ctrl, val, VPSS_UNITE_LEFT); - rkvpss_idx_clear_bits(dev, reg_ctrl, val, VPSS_UNITE_RIGHT); - } - if (sync) { - val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id); - val |= RKVPSS_CROP_GEN_UPD; - rkvpss_idx_write(dev, reg_upd, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, reg_upd, val, VPSS_UNITE_RIGHT); - } - } - stream->is_crop_upd = false; - return 0; -} - -static void poly_phase_scale(struct rkvpss_stream *stream, bool on, bool sync) -{ - struct rkvpss_device *dev = stream->dev; - u32 out_w = stream->out_fmt.width; - u32 out_h = stream->out_fmt.height; - u32 in_w = stream->crop.width; - u32 in_h = stream->crop.height; - u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac; - u32 i, j, idx, ratio, val, in_div, out_div, factor; - bool dering_en = false, yuv420_in = false, yuv422_to_420 = false; - - if (!on) { - rkvpss_unite_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0); - if (dev->unite_mode) { - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0, VPSS_UNITE_RIGHT); - } - return; - } - - /*config scl clk gate*/ - if (in_w == out_w && in_h == out_h) - rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS); - else - rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS, - RKVPSS_SCL0_CKG_DIS); - - /* TODO diff for input and output format */ - if (yuv420_in) { - in_div = 2; - out_div = 2; - } else if (yuv422_to_420) { - in_div = 1; - out_div = 2; - } else { - in_div = 1; - out_div = 1; - } - - if (dev->unite_mode) { - /* unite left */ - if (in_w == out_w) - val = (in_w / 2 - 1) | ((in_h - 1) << 16); - else - val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL - 1) | - ((in_h - 1) << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val, VPSS_UNITE_LEFT); - - /* unite right */ - val = (ALIGN(stream->unite_params.right_scl_need_size_y + 3, 2) - 1) | - ((in_h - 1) << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val, VPSS_UNITE_RIGHT); - val = (ALIGN(stream->unite_params.right_scl_need_size_c + 6, 2) - 1) | - ((in_h - 1) << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val, VPSS_UNITE_RIGHT); - - val = (out_w / 2 - 1) | ((out_h - 1) << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_DST_SIZE, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_DST_SIZE, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_DST_SIZE, val, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_DST_SIZE, val, VPSS_UNITE_RIGHT); - } else { - val = (in_w - 1) | ((in_h - 1) << 16); - rkvpss_unite_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val); - val = (out_w - 1) | ((out_h - 1) << 16); - rkvpss_unite_write(dev, RKVPSS_ZME_Y_DST_SIZE, val); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_DST_SIZE, val); - } - - ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; - if (dering_en) { - ctrl |= RKVPSS_ZME_DERING_EN; - rkvpss_unite_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410); - - if (dev->unite_mode) { - rkvpss_idx_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410, VPSS_UNITE_RIGHT); - } - } - - if (in_w != out_w) { - if (in_w > out_w) { - factor = 4096; - ctrl |= RKVPSS_ZME_XSD_EN; - } else { - factor = 65536; - ctrl |= RKVPSS_ZME_XSU_EN; - } - y_xscl_fac = (in_w - 1) * factor / (out_w - 1); - uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1); - - ratio = y_xscl_fac * 10000 / factor; - idx = rkvpss_get_zme_tap_coe_index(ratio); - for (i = 0; i < 17; i++) { - for (j = 0; j < 8; j += 2) { - val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j], - rkvpss_zme_tap8_coe[idx][i][j + 1]); - rkvpss_unite_write(dev, RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, val); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, val); - - if (dev->unite_mode) { - rkvpss_idx_write(dev, - RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, - RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, - RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, - RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_RIGHT); - } - } - } - } else { - y_xscl_fac = 0; - uv_xscl_fac = 0; - } - - if (dev->unite_mode) { - /* unite left */ - rkvpss_idx_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac, VPSS_UNITE_LEFT); - - /* unite right */ - val = y_xscl_fac | (stream->unite_params.y_w_phase << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, val, VPSS_UNITE_RIGHT); - val = uv_xscl_fac | (stream->unite_params.c_w_phase << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, val, VPSS_UNITE_RIGHT); - } else { - rkvpss_unite_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); - } - - if (in_h != out_h) { - if (in_h > out_h) { - factor = 4096; - ctrl |= RKVPSS_ZME_YSD_EN; - } else { - factor = 65536; - ctrl |= RKVPSS_ZME_YSU_EN; - } - y_yscl_fac = (in_h - 1) * factor / (out_h - 1); - uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1); - - ratio = y_yscl_fac * 10000 / factor; - idx = rkvpss_get_zme_tap_coe_index(ratio); - for (i = 0; i < 17; i++) { - for (j = 0; j < 8; j += 2) { - val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j], - rkvpss_zme_tap6_coe[idx][i][j + 1]); - rkvpss_unite_write(dev, RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, val); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, val); - - if (dev->unite_mode) { - rkvpss_idx_write(dev, - RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, - RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, - RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, - RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_RIGHT); - } - } - } - } else { - y_yscl_fac = 0; - uv_yscl_fac = 0; - } - - if (dev->unite_mode) { - /* unite left */ - rkvpss_idx_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl, VPSS_UNITE_LEFT); - - /* unite right */ - rkvpss_idx_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl, VPSS_UNITE_RIGHT); - } else { - rkvpss_unite_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac); - rkvpss_unite_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl); - } - - if (dev->unite_mode) { - /* unite left */ - val = out_w / 2; - rkvpss_idx_write(dev, RKVPSS_ZME_H_SIZE, (val << 16) | val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, 0, VPSS_UNITE_LEFT); - - /* unite right */ - rkvpss_idx_write(dev, RKVPSS_ZME_H_SIZE, (val << 16) | val, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, 0, VPSS_UNITE_RIGHT); - val = out_w / 2 - ALIGN_DOWN(out_w / 2, 16); - rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, (3 << 20) | (3 << 16) | - (stream->unite_params.scl_in_crop_w_y << 8) | - (stream->unite_params.scl_in_crop_w_c << 12) | - (val << 4) | val, VPSS_UNITE_RIGHT); - } - - ctrl = RKVPSS_ZME_GATING_EN; - if (yuv420_in) - ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; - if (yuv422_to_420) - ctrl |= RKVPSS_ZME_422TO420_EN; - if (dev->unite_mode) { - /* unite left */ - ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_8K_EN; - rkvpss_idx_write(dev, RKVPSS_ZME_CTRL, ctrl, VPSS_UNITE_LEFT); - - /* unite left */ - ctrl |= RKVPSS_ZME_IN_CLIP_EN; - rkvpss_idx_write(dev, RKVPSS_ZME_CTRL, ctrl, VPSS_UNITE_RIGHT); - } else { - rkvpss_unite_write(dev, RKVPSS_ZME_CTRL, ctrl); - } - - val = RKVPSS_ZME_GEN_UPD; - if (sync) - val |= RKVPSS_ZME_FORCE_UPD; - rkvpss_unite_write(dev, RKVPSS_ZME_UPDATE, val); - if (dev->unite_mode) { - rkvpss_idx_write(dev, RKVPSS_ZME_UPDATE, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UPDATE, val, VPSS_UNITE_RIGHT); - } -} - -static void bilinear_scale(struct rkvpss_stream *stream, bool on, bool sync) -{ - struct rkvpss_device *dev = stream->dev; - u32 out_w = stream->out_fmt.width; - u32 out_h = stream->out_fmt.height; - u32 in_w = stream->crop.width; - u32 in_h = stream->crop.height; - u32 in_div, out_div; - u32 reg, val, ctrl = 0, clk_mask = 0; - bool yuv420_in = false, yuv422_to_420 = false; - - if (!on) { - reg = stream->config->scale.ctrl; - rkvpss_unite_write(dev, reg, 0); - return; - } - - /*config scl clk gate*/ - switch (stream->id) { - case RKVPSS_OUTPUT_CH1: - clk_mask = RKVPSS_SCL1_CKG_DIS; - break; - case RKVPSS_OUTPUT_CH2: - clk_mask = RKVPSS_SCL2_CKG_DIS; - break; - case RKVPSS_OUTPUT_CH3: - clk_mask = RKVPSS_SCL3_CKG_DIS; - break; - default: - return; - } - if (in_w == out_w && in_h == out_h) - rkvpss_unite_clear_bits(dev, RKVPSS_SCL0_CKG_DIS, clk_mask); - else - rkvpss_unite_set_bits(dev, RKVPSS_SCL0_CKG_DIS, clk_mask, clk_mask); - - if (!dev->unite_mode) { - /* TODO diff for input and output format */ - if (yuv420_in) { - in_div = 2; - out_div = 2; - } else if (yuv422_to_420) { - in_div = 1; - out_div = 2; - } else { - in_div = 1; - out_div = 1; - } - - reg = stream->config->scale.hy_offs; - rkvpss_unite_write(dev, reg, 0); - reg = stream->config->scale.hc_offs; - rkvpss_unite_write(dev, reg, 0); - reg = stream->config->scale.vy_offs; - rkvpss_unite_write(dev, reg, 0); - reg = stream->config->scale.vc_offs; - rkvpss_unite_write(dev, reg, 0); - - val = in_w | (in_h << 16); - reg = stream->config->scale.src_size; - rkvpss_unite_write(dev, reg, val); - val = out_w | (out_h << 16); - reg = stream->config->scale.dst_size; - rkvpss_unite_write(dev, reg, val); - - if (in_w != out_w) { - val = (in_w - 1) * 4096 / (out_w - 1); - reg = stream->config->scale.hy_fac; - rkvpss_unite_write(dev, reg, val); - - val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); - reg = stream->config->scale.hc_fac; - rkvpss_unite_write(dev, reg, val); - - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - if (in_h != out_h) { - val = (in_h - 1) * 4096 / (out_h - 1); - reg = stream->config->scale.vy_fac; - rkvpss_unite_write(dev, reg, val); - - val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); - reg = stream->config->scale.vc_fac; - rkvpss_unite_write(dev, reg, val); - - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; - } - - reg = stream->config->scale.ctrl; - rkvpss_unite_write(dev, reg, ctrl); - - val = RKVPSS_SCL_GEN_UPD; - if (sync) - val |= RKVPSS_SCL_FORCE_UPD; - reg = stream->config->scale.update; - rkvpss_unite_write(dev, reg, val); - } else { - /* unite left */ - reg = stream->config->scale.in_crop_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - - reg = stream->config->scale.hy_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - reg = stream->config->scale.hc_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - reg = stream->config->scale.vy_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - reg = stream->config->scale.vc_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - - reg = stream->config->scale.hy_offs_mi; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - reg = stream->config->scale.hc_offs_mi; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - - if (in_w == out_w) - val = (in_w / 2) | (in_h << 16); - else - val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL) | (in_h << 16); - reg = stream->config->scale.src_size; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); - val = (out_w / 2) | (out_h << 16); - reg = stream->config->scale.dst_size; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); - - ctrl |= RKVPSS_SCL_CLIP_EN; - - if (in_w != out_w) { - reg = stream->config->scale.hy_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.y_w_fac, VPSS_UNITE_LEFT); - reg = stream->config->scale.hc_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.c_w_fac, VPSS_UNITE_LEFT); - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - - if (in_h != out_h) { - reg = stream->config->scale.vy_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.y_h_fac, VPSS_UNITE_LEFT); - reg = stream->config->scale.vc_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.c_h_fac, VPSS_UNITE_LEFT); - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; - } - - reg = stream->config->scale.ctrl; - rkvpss_idx_write(dev, reg, ctrl, VPSS_UNITE_LEFT); - - val = RKVPSS_SCL_GEN_UPD; - if (sync) - val |= RKVPSS_SCL_FORCE_UPD; - reg = stream->config->scale.update; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); - - /* unite right */ - ctrl = 0; - val = stream->unite_params.scl_in_crop_w_y | - (stream->unite_params.scl_in_crop_w_c << 4); - reg = stream->config->scale.in_crop_offs; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - - val = stream->unite_params.y_w_phase; - reg = stream->config->scale.hy_offs; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - val = stream->unite_params.c_w_phase; - reg = stream->config->scale.hc_offs; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - reg = stream->config->scale.vy_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_RIGHT); - reg = stream->config->scale.vc_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_RIGHT); - - val = out_w / 2 - ALIGN_DOWN(out_w / 2, 16); - reg = stream->config->scale.hy_offs_mi; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - reg = stream->config->scale.hc_offs_mi; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - - val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL) | (in_h << 16); - reg = stream->config->scale.src_size; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - val = (out_w / 2) | (out_h << 16); - reg = stream->config->scale.dst_size; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - - ctrl |= RKVPSS_SCL_CLIP_EN | RKVPSS_SCL_IN_CLIP_EN; - - if (in_w != out_w) { - reg = stream->config->scale.hy_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.y_w_fac, VPSS_UNITE_RIGHT); - reg = stream->config->scale.hc_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.c_w_fac, VPSS_UNITE_RIGHT); - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - - if (in_h != out_h) { - reg = stream->config->scale.vy_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.y_h_fac, VPSS_UNITE_RIGHT); - reg = stream->config->scale.vc_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.c_h_fac, VPSS_UNITE_RIGHT); - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; - } - - reg = stream->config->scale.ctrl; - rkvpss_idx_write(dev, reg, ctrl, VPSS_UNITE_RIGHT); - - val = RKVPSS_SCL_GEN_UPD; - if (sync) - val |= RKVPSS_SCL_FORCE_UPD; - reg = stream->config->scale.update; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - } -} - -static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync) -{ - if (stream->id == RKVPSS_OUTPUT_CH0) - poly_phase_scale(stream, on, sync); - else - bilinear_scale(stream, on, sync); - return 0; -} - -static void rkvpss_stream_stop(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - int ret; - - stream->stopping = true; - if (atomic_read(&dev->pipe_stream_cnt) > 0) { - ret = wait_event_timeout(stream->done, !stream->streaming, - msecs_to_jiffies(300)); - if (!ret) - v4l2_warn(&dev->v4l2_dev, "%s id:%d timeout\n", __func__, stream->id); - } - stream->stopping = false; - stream->streaming = false; - if (stream->ops->disable_mi) - stream->ops->disable_mi(stream); - rkvpss_stream_crop(stream, false, true); - rkvpss_stream_scale(stream, false, true); -} - -static void rkvpss_stop_streaming(struct vb2_queue *queue) -{ - struct rkvpss_stream *stream = queue->drv_priv; - struct rkvpss_vdev_node *node = &stream->vnode; - struct rkvpss_device *dev = stream->dev; - struct rkvpss_hw_dev *hw = dev->hw_dev; - - if (!stream->streaming) - return; - - mutex_lock(&hw->dev_lock); - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s %s id:%d enter\n", __func__, - node->vdev.name, stream->id); - - if (atomic_read(&dev->pipe_stream_cnt) == 1) { - rkvpss_pipeline_stream(dev, false); - rkvpss_stream_stop(stream); - } else { - rkvpss_stream_stop(stream); - rkvpss_pipeline_stream(dev, false); - } - destroy_buf_queue(stream, VB2_BUF_STATE_ERROR); - rkvpss_pipeline_close(dev); - tasklet_disable(&stream->buf_done_tasklet); - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s %s id:%d exit\n", __func__, - node->vdev.name, stream->id); - mutex_unlock(&hw->dev_lock); -} - -static int check_wr_uvswap(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - struct rkvpss_stream *check_stream; - struct capture_fmt *fmt; - bool wr_uv_swap = false; - int i, ret = 0; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - check_stream = &dev->stream_vdev.stream[i]; - if (check_stream->streaming) { - fmt = &check_stream->out_cap_fmt; - switch (fmt->fourcc) { - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_VYUY: - wr_uv_swap = true; - break; - default: - break; - } - } - } - if (wr_uv_swap) { - switch (stream->out_cap_fmt.fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_UYVY: - v4l2_err(&dev->v4l2_dev, "wr_uv_swap need to be consistent\n"); - ret = -EINVAL; - break; - default: - break; - } - } - - return ret; -} - -static int rkvpss_stream_start(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; + struct rkvpss_device *vpss = stream->dev; int ret = 0; - if (dev->unite_mode) - calc_unite_scl_params(stream); - - stream->is_crop_upd = true; - ret = rkvpss_stream_scale(stream, true, true); - if (ret < 0) - return ret; - ret = rkvpss_stream_crop(stream, true, true); - if (ret < 0) - return ret; - ret = check_wr_uvswap(stream); - if (ret < 0) - return ret; - - if (stream->ops->config_mi) - stream->ops->config_mi(stream); - - stream->streaming = true; - stream->dbg.id = -1; - return ret; -} - -static int rkvpss_start_streaming(struct vb2_queue *queue, unsigned int count) -{ - struct rkvpss_stream *stream = queue->drv_priv; - struct rkvpss_vdev_node *node = &stream->vnode; - struct rkvpss_device *dev = stream->dev; - struct rkvpss_hw_dev *hw = dev->hw_dev; - int ret = -EINVAL; - - if (stream->streaming) - return -EBUSY; - - mutex_lock(&hw->dev_lock); - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s %s id:%d enter\n", __func__, - node->vdev.name, stream->id); - - stream->is_pause = true; - if (!dev->inp || !stream->linked) { - v4l2_err(&dev->v4l2_dev, "no link or invalid input source\n"); - goto free_buf_queue; - } - - if (hw->is_ofl_ch[stream->id]) { - v4l2_err(&dev->v4l2_dev, "channel[%d] already assigned to offline", stream->id); - goto free_buf_queue; - } - - rkvpss_pipeline_open(dev); - - ret = rkvpss_stream_start(stream); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "start %s failed\n", node->vdev.name); - goto pipe_close; - } - - ret = rkvpss_pipeline_stream(dev, true); - if (ret < 0) - goto stop_stream; - - tasklet_enable(&stream->buf_done_tasklet); - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s %s id:%d exit\n", __func__, - node->vdev.name, stream->id); - mutex_unlock(&hw->dev_lock); - return 0; -stop_stream: - stream->streaming = false; - rkvpss_stream_stop(stream); -pipe_close: - rkvpss_pipeline_close(dev); -free_buf_queue: - destroy_buf_queue(stream, VB2_BUF_STATE_QUEUED); - mutex_unlock(&hw->dev_lock); - return ret; -} - -static const struct vb2_ops stream_vb2_ops = { - .queue_setup = rkvpss_queue_setup, - .buf_queue = rkvpss_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .stop_streaming = rkvpss_stop_streaming, - .start_streaming = rkvpss_start_streaming, -}; - -static int rkvpss_init_vb2_queue(struct vb2_queue *q, - struct rkvpss_stream *stream, - enum v4l2_buf_type buf_type) -{ - q->type = buf_type; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; - q->drv_priv = stream; - q->ops = &stream_vb2_ops; - q->mem_ops = stream->dev->hw_dev->mem_ops; - q->buf_struct_size = sizeof(struct rkvpss_buffer); - q->min_buffers_needed = STREAM_OUT_REQ_BUFS_MIN; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - - q->lock = &stream->dev->apilock; - q->dev = stream->dev->hw_dev->dev; - q->allow_cache_hints = 1; - q->bidirectional = 1; - if (stream->dev->hw_dev->is_dma_contig) - q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; - q->gfp_flags = GFP_DMA32; - return vb2_queue_init(q); -} - -static const -struct capture_fmt *find_fmt(struct rkvpss_stream *stream, u32 pixelfmt) -{ - const struct capture_fmt *fmt; - u32 i; - - for (i = 0; i < stream->config->fmt_size; i++) { - fmt = &stream->config->fmts[i]; - if (fmt->fourcc == pixelfmt) - return fmt; - } - return NULL; -} - -static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs) -{ - switch (fcc) { - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_YUV444M: - *xsubs = 1; - *ysubs = 1; - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_VYUY: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUV422P: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_YVU422M: - case V4L2_PIX_FMT_FBC2: - *xsubs = 2; - *ysubs = 1; - break; - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21M: - case V4L2_PIX_FMT_NV12M: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_FBC0: - *xsubs = 2; - *ysubs = 2; - break; - default: - return -EINVAL; - } - return 0; -} - -static int rkvpss_set_fmt(struct rkvpss_stream *stream, - struct v4l2_pix_format_mplane *pixm, - bool try) -{ - struct rkvpss_device *dev = stream->dev; - u32 i, planes, xsubs = 1, ysubs = 1, imagsize = 0; - const struct capture_fmt *fmt; - - fmt = find_fmt(stream, pixm->pixelformat); - if (!fmt) { - v4l2_err(&dev->v4l2_dev, - "nonsupport pixelformat:%c%c%c%c\n", - pixm->pixelformat, - pixm->pixelformat >> 8, - pixm->pixelformat >> 16, - pixm->pixelformat >> 24); - return -EINVAL; - } - - /* Tile4x4 writing of Channel0 and Channel1 only supports either one.*/ - if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) { - if (stream->id == 0) { - if (dev->stream_vdev.stream[1].streaming && - (dev->stream_vdev.stream[1].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || - dev->stream_vdev.stream[1].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)) { - v4l2_err(&dev->v4l2_dev, - "Tile4x4 writing of Ch0 and Cl1 only supports either one\n"); - return -EINVAL; - } - } - if (stream->id == 1) { - if (dev->stream_vdev.stream[0].streaming && - (dev->stream_vdev.stream[0].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || - dev->stream_vdev.stream[0].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)) { - v4l2_err(&dev->v4l2_dev, - "Tile4x4 writing of Ch0 and Cl1 only supports either one\n"); - return -EINVAL; - } - } - } - - pixm->num_planes = fmt->mplanes; - pixm->field = V4L2_FIELD_NONE; - if (!pixm->quantization) - pixm->quantization = V4L2_QUANTIZATION_FULL_RANGE; - fcc_xysubs(fmt->fourcc, &xsubs, &ysubs); - planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes; - for (i = 0; i < planes; i++) { - struct v4l2_plane_pix_format *plane_fmt; - u32 width, height, bytesperline, w, h; - - plane_fmt = pixm->plane_fmt + i; - w = pixm->width; - h = pixm->height; - width = i ? w / xsubs : w; - height = i ? h / ysubs : h; - - if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) - bytesperline = ALIGN(((width / 4) * fmt->bpp[i]), 16); - else - bytesperline = width * DIV_ROUND_UP(fmt->bpp[i], 8); - - if (i != 0 || plane_fmt->bytesperline < bytesperline) - plane_fmt->bytesperline = bytesperline; - - if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) - plane_fmt->sizeimage = plane_fmt->bytesperline * (height / 4); - else - plane_fmt->sizeimage = plane_fmt->bytesperline * height; - - imagsize += plane_fmt->sizeimage; - } - if (fmt->mplanes == 1) - pixm->plane_fmt[0].sizeimage = imagsize; - if (!try) { - stream->out_cap_fmt = *fmt; - stream->out_fmt = *pixm; - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s: stream:%d req(%d, %d) out(%d, %d)\n", - __func__, stream->id, pixm->width, pixm->height, - stream->out_fmt.width, stream->out_fmt.height); - } - return 0; -} - -/************************* v4l2_file_operations***************************/ - -static int rkvpss_fh_open(struct file *file) -{ - struct rkvpss_stream *stream = video_drvdata(file); - struct rkvpss_device *dev = stream->dev; - int ret; - - if (!dev->is_probe_end) - return -EINVAL; - - ret = v4l2_fh_open(file); - if (!ret) { - ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, - "pipeline power on failed %d\n", ret); - vb2_fop_release(file); - } - } - return ret; -} - -static int rkvpss_fh_release(struct file *file) -{ - struct rkvpss_stream *stream = video_drvdata(file); - int ret; - - ret = vb2_fop_release(file); - if (!ret) - v4l2_pipeline_pm_put(&stream->vnode.vdev.entity); - return ret; -} - -static const struct v4l2_file_operations rkvpss_fops = { - .open = rkvpss_fh_open, - .release = rkvpss_fh_release, - .unlocked_ioctl = video_ioctl2, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, -#ifdef CONFIG_COMPAT - .compat_ioctl32 = video_ioctl2, -#endif -}; - -static int rkvpss_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - if (input->index > 0) - return -EINVAL; - - input->type = V4L2_INPUT_TYPE_CAMERA; - strscpy(input->name, "Camera", sizeof(input->name)); - - return 0; -} - -static int rkvpss_try_fmt_vid_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct rkvpss_stream *stream = video_drvdata(file); - - return rkvpss_set_fmt(stream, &f->fmt.pix_mp, true); -} - -static int rkvpss_enum_fmt_vid_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct rkvpss_stream *stream = video_drvdata(file); - const struct capture_fmt *fmt = NULL; - - if (f->index >= stream->config->fmt_size) - return -EINVAL; - - fmt = &stream->config->fmts[f->index]; - f->pixelformat = fmt->fourcc; - - switch (f->pixelformat) { - case V4L2_PIX_FMT_TILE420: - strscpy(f->description, - "Rockchip yuv420 tile", - sizeof(f->description)); - break; - case V4L2_PIX_FMT_TILE422: - strscpy(f->description, - "Rockchip yuv422 tile", - sizeof(f->description)); - break; - default: - break; - } - - return 0; -} - -static int rkvpss_s_fmt_vid_mplane(struct file *file, - void *priv, struct v4l2_format *f) -{ - struct rkvpss_stream *stream = video_drvdata(file); - struct video_device *vdev = &stream->vnode.vdev; - struct rkvpss_vdev_node *node = vdev_to_node(vdev); - struct rkvpss_device *dev = stream->dev; - - /* Change not allowed if queue is streaming. */ - if (vb2_is_streaming(&node->buf_queue)) { - v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); - return -EBUSY; - } - - return rkvpss_set_fmt(stream, &f->fmt.pix_mp, false); -} - -static int rkvpss_g_fmt_vid_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct rkvpss_stream *stream = video_drvdata(file); - - f->fmt.pix_mp = stream->out_fmt; - - return 0; -} - -static int rkvpss_g_selection(struct file *file, void *prv, - struct v4l2_selection *sel) -{ - struct rkvpss_stream *stream = video_drvdata(file); - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - sel->r = stream->crop; - break; - default: - return -EINVAL; - } - return 0; -} - -static int rkvpss_s_selection(struct file *file, void *prv, - struct v4l2_selection *sel) -{ - struct rkvpss_stream *stream = video_drvdata(file); - struct rkvpss_device *dev = stream->dev; - struct v4l2_rect *crop = &stream->crop; - u32 max_w = dev->vpss_sdev.in_fmt.width; - u32 max_h = dev->vpss_sdev.in_fmt.height; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - if (sel->flags != 0) - return -EINVAL; - - sel->r.left = ALIGN(sel->r.left, 2); - sel->r.top = ALIGN(sel->r.top, 2); - sel->r.width = ALIGN(sel->r.width, 2); - sel->r.height = ALIGN(sel->r.height, 2); - if (!sel->r.width || !sel->r.height || - sel->r.width + sel->r.left > max_w || - sel->r.height + sel->r.top > max_h) { - v4l2_err(&dev->v4l2_dev, - "invalid crop left:%d top:%d w:%d h:%d for %dx%d\n", - sel->r.left, sel->r.top, sel->r.width, sel->r.height, max_w, max_h); - return -EINVAL; - } - - *crop = sel->r; - stream->is_crop_upd = true; - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "stream %d crop(%d,%d)/%dx%d\n", stream->id, - crop->left, crop->top, crop->width, crop->height); - return 0; -} - -static int rkvpss_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct rkvpss_stream *stream = video_drvdata(file); - struct device *dev = stream->dev->dev; - struct video_device *vdev = video_devdata(file); - - strscpy(cap->card, vdev->name, sizeof(cap->card)); - snprintf(cap->driver, sizeof(cap->driver), - "%s_v%d", dev->driver->name, - stream->dev->vpss_ver >> 4); - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(dev)); - - return 0; -} - -static void rkvpss_stream_mf(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - u32 val, mask; - - if (!stream->is_mf_upd) - return; - stream->is_mf_upd = false; - - if (!dev->hw_dev->is_ofl_cmsc) { - mask = RKVPSS_MIR_EN; - val = dev->mir_en ? RKVPSS_MIR_EN : 0; - rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CTRL, mask, val); - } - mask = RKVPSS_MI_CHN_V_FLIP(stream->id); - /* Tile4x4 writing can't flip*/ - if (stream->out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || - stream->out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422) - stream->flip_en = false; - val = stream->flip_en ? mask : 0; - rkvpss_unite_set_bits(dev, RKVPSS_MI_WR_VFLIP_CTRL, mask, val); -} - -static int rkvpss_get_mirror_flip(struct rkvpss_stream *stream, - struct rkvpss_mirror_flip *cfg) -{ - cfg->mirror = stream->dev->mir_en; - cfg->flip = stream->flip_en; - return 0; -} - -static int rkvpss_set_mirror_flip(struct rkvpss_stream *stream, - struct rkvpss_mirror_flip *cfg) -{ - struct rkvpss_device *dev = stream->dev; - - if (dev->hw_dev->is_ofl_cmsc && cfg->mirror) { - cfg->mirror = 0; - v4l2_warn(&stream->dev->v4l2_dev, - "mirror mux to vpss offline mode\n"); - } - if (dev->mir_en != !!cfg->mirror || - stream->flip_en != !!cfg->flip) - stream->is_mf_upd = true; - dev->mir_en = !!cfg->mirror; - stream->flip_en = !!cfg->flip; - return 0; -} - -static void cmsc_config_hw(struct rkvpss_device *dev, struct rkvpss_cmsc_cfg *cfg, bool sync, - int unite_index) -{ - int i, j, k, slope, hor; - u32 win_en, mode, val, ctrl = 0; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - win_en = cfg->win[i].win_en; - v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, - "%s unite:%d, unite_index:%d ch:%d win_en:0x%x\n", __func__, - dev->unite_mode, unite_index, i, win_en); - if (win_en) - ctrl |= RKVPSS_CMSC_CHN_EN(i); - rkvpss_idx_write(dev, RKVPSS_CMSC_CHN0_WIN + i * 4, win_en, unite_index); - - mode = cfg->win[i].mode; - rkvpss_idx_write(dev, RKVPSS_CMSC_CHN0_MODE + i * 4, mode, unite_index); - - for (j = 0; j < RKVPSS_CMSC_WIN_MAX && win_en; j++) { - if (!(win_en & BIT(j))) - continue; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - val = RKVPSS_CMSC_WIN_VTX(cfg->win[j].point[k].x, - cfg->win[j].point[k].y); - rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val, - unite_index); - - rkvpss_cmsc_slop(&cfg->win[j].point[k], - (k + 1 == RKVPSS_CMSC_POINT_MAX) ? - &cfg->win[j].point[0] : &cfg->win[j].point[k + 1], - &slope, &hor); - val = RKVPSS_CMSC_WIN_SLP(slope, hor); - rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val, - unite_index); - v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, - "%s unite:%d unite_index:%d ch:%d win:%d point:%d x:%u y:%u", - __func__, dev->unite_mode, unite_index, i, j, k, - cfg->win[j].point[k].x, - cfg->win[j].point[k].y); - } - - if ((mode & BIT(j))) - continue; - val = RKVPSS_CMSK_WIN_YUV(cfg->win[j].cover_color_y, - cfg->win[j].cover_color_u, - cfg->win[j].cover_color_v); - val |= RKVPSS_CMSC_WIN_ALPHA(cfg->win[j].cover_color_a); - rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_PARA + j * 4, val, unite_index); - } - } - - if (ctrl) { - ctrl |= RKVPSS_CMSC_EN; - ctrl |= RKVPSS_CMSC_BLK_SZIE(cfg->mosaic_block); - } - rkvpss_idx_write(dev, RKVPSS_CMSC_CTRL, ctrl, unite_index); - val = RKVPSS_CMSC_GEN_UPD; - if (sync) - val |= RKVPSS_CMSC_FORCE_UPD; - rkvpss_idx_write(dev, RKVPSS_CMSC_UPDATE, val, unite_index); -} - -void rkvpss_cmsc_config(struct rkvpss_device *dev, bool sync) -{ - unsigned long lock_flags = 0; - struct rkvpss_cmsc_cfg left_cfg = {0}, right_cfg = {0}; - struct rkvpss_cmsc_win *win; - struct rkvpss_cmsc_point *point; - int i, j, k; - u32 mask; - - spin_lock_irqsave(&dev->cmsc_lock, lock_flags); - if (dev->hw_dev->is_ofl_cmsc || !dev->cmsc_upd) { - spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); - return; - } - dev->cmsc_upd = false; - spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); - - if (dev->unite_mode) { - /* unite left */ - for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { - left_cfg.win[i].cover_color_y = dev->cmsc_cfg.win[i].cover_color_y; - left_cfg.win[i].cover_color_u = dev->cmsc_cfg.win[i].cover_color_u; - left_cfg.win[i].cover_color_v = dev->cmsc_cfg.win[i].cover_color_v; - left_cfg.win[i].cover_color_a = dev->cmsc_cfg.win[i].cover_color_a; - for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) - left_cfg.win[i].point[j] = dev->cmsc_cfg.win[i].point[j]; - } - left_cfg.mosaic_block = dev->cmsc_cfg.mosaic_block; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - left_cfg.win[i].win_en = dev->cmsc_cfg.win[i].win_en; - left_cfg.win[i].mode = dev->cmsc_cfg.win[i].mode; - } - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - win = &left_cfg.win[j]; - if (!(left_cfg.win[i].win_en & BIT(j))) - continue; - mask = 0; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - point = &win->point[k]; - if (point->x >= dev->vpss_sdev.in_fmt.width / 2) - mask |= BIT(k); - else - mask &= ~BIT(k); - } - if (mask == 0xf) { - /** all right **/ - left_cfg.win[i].win_en &= ~BIT(j); - } else if (mask != 0) { - /** middle need avoid pentagon **/ - if (win->point[0].x != win->point[3].x || - win->point[1].x != win->point[2].x) { - left_cfg.win[i].win_en &= ~BIT(j); - } else { - win->point[1].x = dev->vpss_sdev.in_fmt.width / 2; - win->point[2].x = dev->vpss_sdev.in_fmt.width / 2; - } - } - } - } - - /* unite right */ - for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { - right_cfg.win[i].cover_color_y = dev->cmsc_cfg.win[i].cover_color_y; - right_cfg.win[i].cover_color_u = dev->cmsc_cfg.win[i].cover_color_u; - right_cfg.win[i].cover_color_v = dev->cmsc_cfg.win[i].cover_color_v; - right_cfg.win[i].cover_color_a = dev->cmsc_cfg.win[i].cover_color_a; - - for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) - right_cfg.win[i].point[j] = dev->cmsc_cfg.win[i].point[j]; - } - right_cfg.mosaic_block = dev->cmsc_cfg.mosaic_block; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - right_cfg.win[i].win_en = dev->cmsc_cfg.win[i].win_en; - right_cfg.win[i].mode = dev->cmsc_cfg.win[i].mode; - } - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - win = &right_cfg.win[j]; - if (!(right_cfg.win[i].win_en & BIT(j))) - continue; - mask = 0; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - point = &win->point[k]; - if (point->x <= dev->vpss_sdev.in_fmt.width / 2) - mask |= BIT(k); - else - mask &= ~BIT(k); - } - if (mask == 0xf) { - /** all left **/ - right_cfg.win[i].win_en &= ~BIT(j); - } else if (mask != 0) { - /** middle need avoid pentagon **/ - if (win->point[0].x != win->point[3].x || - win->point[1].x != win->point[2].x) { - right_cfg.win[i].win_en &= ~BIT(j); - } else { - win->point[0].x = RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[3].x = RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[1].x = win->point[1].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[2].x = win->point[2].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - } - } else { - /** all right **/ - win->point[0].x = win->point[0].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[1].x = win->point[1].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[2].x = win->point[2].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[3].x = win->point[3].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - } - } - } - - cmsc_config_hw(dev, &left_cfg, sync, VPSS_UNITE_LEFT); - cmsc_config_hw(dev, &right_cfg, sync, VPSS_UNITE_RIGHT); - } else { - cmsc_config_hw(dev, &dev->cmsc_cfg, sync, VPSS_UNITE_LEFT); - } -} - -static int rkvpss_get_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg) -{ - struct rkvpss_device *dev = stream->dev; - unsigned long lock_flags = 0; - u32 i, win_en, mode; - - spin_lock_irqsave(&dev->cmsc_lock, lock_flags); - *cfg = dev->cmsc_cfg; - spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); - - win_en = cfg->win[stream->id].win_en; - mode = cfg->win[stream->id].mode; - for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { - cfg->win[i].win_en = !!(win_en & BIT(i)); - cfg->win[i].mode = !!(mode & BIT(i)); - } - cfg->width_ro = dev->vpss_sdev.in_fmt.width; - cfg->height_ro = dev->vpss_sdev.in_fmt.height; - return 0; -} - -static int rkvpss_set_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg) -{ - struct rkvpss_device *dev = stream->dev; - unsigned long lock_flags = 0; - u16 i, j, k, win_en = 0, mode = 0; - int ret = 0; - - if (dev->hw_dev->is_ofl_cmsc) - return -EINVAL; - - spin_lock_irqsave(&dev->cmsc_lock, lock_flags); - for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { - if (!cfg->win[i].win_en) - continue; - - win_en |= BIT(i); - mode |= cfg->win[i].mode ? BIT(i) : 0; - - for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) { - for (k = j + 1; k < RKVPSS_CMSC_POINT_MAX; k++) { - if (cfg->win[i].point[j].x == cfg->win[i].point[k].x && - cfg->win[i].point[j].y == cfg->win[i].point[k].y) { - v4l2_warn(&dev->v4l2_dev, - "points should different, P%d(%d,%d) P%d(%d,%d)\n", - j, cfg->win[i].point[j].x, cfg->win[i].point[j].y, - k, cfg->win[i].point[k].x, cfg->win[i].point[k].y); - ret = -EINVAL; - goto unlock; - } - } - } - if (!cfg->win[i].mode) { - dev->cmsc_cfg.win[i].cover_color_y = cfg->win[i].cover_color_y; - dev->cmsc_cfg.win[i].cover_color_u = cfg->win[i].cover_color_u; - dev->cmsc_cfg.win[i].cover_color_v = cfg->win[i].cover_color_v; - dev->cmsc_cfg.win[i].cover_color_a = cfg->win[i].cover_color_a; - if (dev->cmsc_cfg.win[i].cover_color_a > 15) - dev->cmsc_cfg.win[i].cover_color_a = 15; - } - for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) - dev->cmsc_cfg.win[i].point[j] = cfg->win[i].point[j]; - } - dev->cmsc_cfg.win[stream->id].win_en = win_en; - dev->cmsc_cfg.win[stream->id].mode = mode; - dev->cmsc_cfg.mosaic_block = cfg->mosaic_block; - dev->cmsc_upd = true; - v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, - "%s ch:%d win_en:0x%x", - __func__, stream->id, - dev->cmsc_cfg.win[stream->id].win_en); - -unlock: - spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); - return ret; -} - -static long rkvpss_ioctl_default(struct file *file, void *fh, - bool valid_prio, unsigned int cmd, void *arg) -{ - struct rkvpss_stream *stream = video_drvdata(file); - long ret = 0; - - if (!arg) - return -EINVAL; - - switch (cmd) { - case RKVPSS_CMD_GET_MIRROR_FLIP: - ret = rkvpss_get_mirror_flip(stream, arg); - break; - case RKVPSS_CMD_SET_MIRROR_FLIP: - ret = rkvpss_set_mirror_flip(stream, arg); - break; - case RKVPSS_CMD_GET_CMSC: - ret = rkvpss_get_cmsc(stream, arg); - break; - case RKVPSS_CMD_SET_CMSC: - ret = rkvpss_set_cmsc(stream, arg); - break; - case RKVPSS_CMD_STREAM_ATTACH_INFO: - stream->is_attach_info = *(int *)arg; - break; - default: - ret = -EINVAL; - } + if (vpss->vpss_ver == VPSS_V10) + ret = rkvpss_stream_buf_cnt_v1(stream); return ret; } -static const struct v4l2_ioctl_ops rkvpss_v4l2_ioctl_ops = { - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_enum_input = rkvpss_enum_input, - .vidioc_try_fmt_vid_cap_mplane = rkvpss_try_fmt_vid_mplane, - .vidioc_enum_fmt_vid_cap = rkvpss_enum_fmt_vid_mplane, - .vidioc_s_fmt_vid_cap_mplane = rkvpss_s_fmt_vid_mplane, - .vidioc_g_fmt_vid_cap_mplane = rkvpss_g_fmt_vid_mplane, - .vidioc_s_selection = rkvpss_s_selection, - .vidioc_g_selection = rkvpss_g_selection, - .vidioc_querycap = rkvpss_querycap, - .vidioc_default = rkvpss_ioctl_default, -}; - -static void rkvpss_unregister_stream_video(struct rkvpss_stream *stream) -{ - tasklet_kill(&stream->buf_done_tasklet); - media_entity_cleanup(&stream->vnode.vdev.entity); - video_unregister_device(&stream->vnode.vdev); -} - -static int rkvpss_register_stream_video(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; - struct video_device *vdev = &stream->vnode.vdev; - struct rkvpss_vdev_node *node; - enum v4l2_buf_type buf_type; - int ret = 0; - - node = vdev_to_node(vdev); - vdev->release = video_device_release_empty; - vdev->fops = &rkvpss_fops; - vdev->minor = -1; - vdev->v4l2_dev = v4l2_dev; - vdev->lock = &dev->apilock; - video_set_drvdata(vdev, stream); - - vdev->ioctl_ops = &rkvpss_v4l2_ioctl_ops; - vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; - vdev->vfl_dir = VFL_DIR_RX; - node->pad.flags = MEDIA_PAD_FL_SINK; - buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - - rkvpss_init_vb2_queue(&node->buf_queue, stream, buf_type); - vdev->queue = &node->buf_queue; - - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret < 0) { - v4l2_err(v4l2_dev, - "video register failed with error %d\n", ret); - return ret; - } - - ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); - if (ret < 0) - goto unreg; - - INIT_LIST_HEAD(&stream->buf_done_list); - tasklet_init(&stream->buf_done_tasklet, - rkvpss_buf_done_task, (unsigned long)stream); - tasklet_disable(&stream->buf_done_tasklet); - return 0; -unreg: - video_unregister_device(vdev); - return ret; -} - -void rkvpss_stream_default_fmt(struct rkvpss_device *dev, u32 id, - u32 width, u32 height, u32 pixelformat) -{ - struct rkvpss_stream *stream = &dev->stream_vdev.stream[id]; - struct v4l2_pix_format_mplane pixm = { 0 }; - - if (pixelformat) - pixm.pixelformat = pixelformat; - else - pixm.pixelformat = stream->out_cap_fmt.fourcc; - if (!pixm.pixelformat) - return; - - stream->crop.left = 0; - stream->crop.top = 0; - stream->crop.width = width; - stream->crop.height = height; - - pixm.width = width; - pixm.height = height; - rkvpss_set_fmt(stream, &pixm, false); -} - int rkvpss_register_stream_vdevs(struct rkvpss_device *dev) { - struct rkvpss_stream_vdev *stream_vdev; - struct rkvpss_stream *stream; - struct video_device *vdev; - char *vdev_name; - int i, j, ret = 0; + int ret = -EINVAL; - stream_vdev = &dev->stream_vdev; - memset(stream_vdev, 0, sizeof(*stream_vdev)); + if (dev->vpss_ver == VPSS_V10) + ret = rkvpss_register_stream_vdevs_v1(dev); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - stream = &stream_vdev->stream[i]; - stream->id = i; - stream->dev = dev; - vdev = &stream->vnode.vdev; - INIT_LIST_HEAD(&stream->buf_queue); - init_waitqueue_head(&stream->done); - spin_lock_init(&stream->vbq_lock); - switch (i) { - case RKVPSS_OUTPUT_CH0: - vdev_name = S0_VDEV_NAME; - stream->ops = &scl_stream_ops; - stream->config = &scl0_config; - break; - case RKVPSS_OUTPUT_CH1: - vdev_name = S1_VDEV_NAME; - stream->ops = &scl_stream_ops; - stream->config = &scl1_config; - break; - case RKVPSS_OUTPUT_CH2: - vdev_name = S2_VDEV_NAME; - stream->ops = &scl_stream_ops; - stream->config = &scl2_config; - break; - case RKVPSS_OUTPUT_CH3: - vdev_name = S3_VDEV_NAME; - stream->ops = &scl_stream_ops; - stream->config = &scl3_config; - break; - default: - v4l2_err(&dev->v4l2_dev, "Invalid stream:%d\n", i); - return -EINVAL; - } - strscpy(vdev->name, vdev_name, sizeof(vdev->name)); - ret = rkvpss_register_stream_video(stream); - if (ret < 0) - goto err; - } - return 0; -err: - for (j = 0; j < i; j++) { - stream = &stream_vdev->stream[j]; - rkvpss_unregister_stream_video(stream); - } return ret; } void rkvpss_unregister_stream_vdevs(struct rkvpss_device *dev) { - struct rkvpss_stream_vdev *stream_vdev; - struct rkvpss_stream *stream; - int i; + if (dev->vpss_ver == VPSS_V10) + rkvpss_unregister_stream_vdevs_v1(dev); +} - stream_vdev = &dev->stream_vdev; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - stream = &stream_vdev->stream[i]; - rkvpss_unregister_stream_video(stream); - } +void rkvpss_stream_default_fmt(struct rkvpss_device *dev, u32 id, + u32 width, u32 height, u32 pixelformat) +{ + if (dev->vpss_ver == VPSS_V10) + rkvpss_stream_default_fmt_v1(dev, id, width, height, pixelformat); } void rkvpss_isr(struct rkvpss_device *dev, u32 mis_val) { - v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, - "isr:0x%x\n", mis_val); - dev->isr_cnt++; - if (mis_val & RKVPSS_ISP_ALL_FRM_END && dev->remote_sd) - rkvpss_check_idle(dev, VPSS_FRAME_END); + if (dev->vpss_ver == VPSS_V10) + rkvpss_isr_v1(dev, mis_val); } void rkvpss_mi_isr(struct rkvpss_device *dev, u32 mis_val) { - struct rkvpss_stream *stream; - int i, ris = readl(dev->hw_dev->base_addr + RKVPSS_MI_RIS); - - v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, - "mi isr:0x%x, ris:0x%x\n", mis_val, ris); - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - stream = &dev->stream_vdev.stream[i]; - - if (!stream->streaming || - !(ris & stream->config->frame_end_id)) - continue; - writel(stream->config->frame_end_id, dev->hw_dev->base_addr + RKVPSS_MI_ICR); - - if (!dev->unite_mode || dev->unite_index == VPSS_UNITE_RIGHT) { - if (stream->stopping) { - stream->stopping = false; - stream->streaming = false; - stream->ops->disable_mi(stream); - wake_up(&stream->done); - } else { - rkvpss_frame_end(stream); - } - } - } - rkvpss_check_idle(dev, (ris & 0xf) << 3); + if (dev->vpss_ver == VPSS_V10) + rkvpss_mi_isr_v1(dev, mis_val); } diff --git a/drivers/media/platform/rockchip/vpss/stream.h b/drivers/media/platform/rockchip/vpss/stream.h index 3a54d5e1c71b..d8bee7c96a8b 100644 --- a/drivers/media/platform/rockchip/vpss/stream.h +++ b/drivers/media/platform/rockchip/vpss/stream.h @@ -4,8 +4,23 @@ #ifndef _RKVPSS_STREAM_H #define _RKVPSS_STREAM_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include -#include "common.h" + +#define STREAM_OUT_REQ_BUFS_MIN 0 struct rkvpss_stream; diff --git a/drivers/media/platform/rockchip/vpss/stream_v1.c b/drivers/media/platform/rockchip/vpss/stream_v1.c new file mode 100644 index 000000000000..939bd68f6910 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/stream_v1.c @@ -0,0 +1,2823 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ + +#include "vpss.h" +#include "common.h" +#include "stream.h" +#include "dev.h" +#include "vpss_offline.h" +#include "hw.h" +#include "procfs.h" +#include "regs_v1.h" + +#include "stream_v1.h" + + +#define STREAM_OUT_REQ_BUFS_MIN 0 + +static void rkvpss_frame_end(struct rkvpss_stream *stream); +static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync); +static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync); +static void rkvpss_stream_mf(struct rkvpss_stream *stream); + +static const struct capture_fmt scl0_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_GREY, + .fmt_type = FMT_YUV, + .bpp = { 8 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_TILE420, + .fmt_type = FMT_YUV, + .bpp = { 24 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_TILE422, + .fmt_type = FMT_YUV, + .bpp = { 32 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + } +}; + +static const struct capture_fmt scl1_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_GREY, + .fmt_type = FMT_YUV, + .bpp = { 8 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + .fmt_type = FMT_RGB, + .bpp = { 16 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = RKVPSS_MI_CHN_WR_RB_SWAP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .fmt_type = FMT_RGB, + .bpp = { 24 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = RKVPSS_MI_CHN_WR_RB_SWAP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888, + }, { + .fourcc = V4L2_PIX_FMT_XBGR32, + .fmt_type = FMT_RGB, + .bpp = { 32 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888, + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, + .fmt_type = FMT_RGB, + .bpp = { 16 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .fmt_type = FMT_RGB, + .bpp = { 24 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888, + }, { + .fourcc = V4L2_PIX_FMT_XRGB32, + .fmt_type = FMT_RGB, + .bpp = { 32 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = RKVPSS_MI_CHN_WR_RB_SWAP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888, + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_TILE420, + .fmt_type = FMT_YUV, + .bpp = { 24 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_TILE422, + .fmt_type = FMT_YUV, + .bpp = { 32 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + } +}; + +static const struct capture_fmt scl2_3_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_GREY, + .fmt_type = FMT_YUV, + .bpp = { 8 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + } +}; + + +static struct stream_config scl0_config = { + .fmts = scl0_fmts, + .fmt_size = ARRAY_SIZE(scl0_fmts), + .frame_end_id = RKVPSS_MI_CHN0_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_0_H_OFFS, + .v_offs = RKVPSS_CROP1_0_V_OFFS, + .h_size = RKVPSS_CROP1_0_H_SIZE, + .v_size = RKVPSS_CROP1_0_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_0_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_0_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_0_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_0_V_SIZE_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN0_WR_CTRL, + .stride = RKVPSS_MI_CHN0_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN0_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN0_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN0_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN0_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN0_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN0_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN0_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN0_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN0_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN0_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN0_WR_CB_BASE_SHD, + }, +}; + +static struct stream_config scl1_config = { + .fmts = scl1_fmts, + .fmt_size = ARRAY_SIZE(scl1_fmts), + .frame_end_id = RKVPSS_MI_CHN1_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_1_H_OFFS, + .v_offs = RKVPSS_CROP1_1_V_OFFS, + .h_size = RKVPSS_CROP1_1_H_SIZE, + .v_size = RKVPSS_CROP1_1_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_1_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_1_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_1_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_1_V_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS_SCALE1_CTRL, + .update = RKVPSS_SCALE1_UPDATE, + .src_size = RKVPSS_SCALE1_SRC_SIZE, + .dst_size = RKVPSS_SCALE1_DST_SIZE, + .hy_fac = RKVPSS_SCALE1_HY_FAC, + .hc_fac = RKVPSS_SCALE1_HC_FAC, + .vy_fac = RKVPSS_SCALE1_VY_FAC, + .vc_fac = RKVPSS_SCALE1_VC_FAC, + .hy_offs = RKVPSS_SCALE1_HY_OFFS, + .hc_offs = RKVPSS_SCALE1_HC_OFFS, + .vy_offs = RKVPSS_SCALE1_VY_OFFS, + .vc_offs = RKVPSS_SCALE1_VC_OFFS, + .hy_size = RKVPSS_SCALE1_HY_SIZE, + .hc_size = RKVPSS_SCALE1_HC_SIZE, + .hy_offs_mi = RKVPSS_SCALE1_HY_OFFS_MI, + .hc_offs_mi = RKVPSS_SCALE1_HC_OFFS_MI, + .in_crop_offs = RKVPSS_SCALE1_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS_SCALE1_CTRL_SHD, + .src_size_shd = RKVPSS_SCALE1_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS_SCALE1_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS_SCALE1_HY_FAC_SHD, + .hc_fac_shd = RKVPSS_SCALE1_HC_FAC_SHD, + .vy_fac_shd = RKVPSS_SCALE1_VY_FAC_SHD, + .vc_fac_shd = RKVPSS_SCALE1_VC_FAC_SHD, + .hy_offs_shd = RKVPSS_SCALE1_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS_SCALE1_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS_SCALE1_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS_SCALE1_VC_OFFS_SHD, + .hy_size_shd = RKVPSS_SCALE1_HY_SIZE_SHD, + .hc_size_shd = RKVPSS_SCALE1_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS_SCALE1_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS_SCALE1_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS_SCALE1_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN1_WR_CTRL, + .stride = RKVPSS_MI_CHN1_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN1_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN1_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN1_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN1_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN1_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN1_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN1_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN1_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN1_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN1_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN1_WR_CB_BASE_SHD, + }, +}; + +static struct stream_config scl2_config = { + .fmts = scl2_3_fmts, + .fmt_size = ARRAY_SIZE(scl2_3_fmts), + .frame_end_id = RKVPSS_MI_CHN2_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_2_H_OFFS, + .v_offs = RKVPSS_CROP1_2_V_OFFS, + .h_size = RKVPSS_CROP1_2_H_SIZE, + .v_size = RKVPSS_CROP1_2_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_2_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_2_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_2_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_2_V_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS_SCALE2_CTRL, + .update = RKVPSS_SCALE2_UPDATE, + .src_size = RKVPSS_SCALE2_SRC_SIZE, + .dst_size = RKVPSS_SCALE2_DST_SIZE, + .hy_fac = RKVPSS_SCALE2_HY_FAC, + .hc_fac = RKVPSS_SCALE2_HC_FAC, + .vy_fac = RKVPSS_SCALE2_VY_FAC, + .vc_fac = RKVPSS_SCALE2_VC_FAC, + .hy_offs = RKVPSS_SCALE2_HY_OFFS, + .hc_offs = RKVPSS_SCALE2_HC_OFFS, + .vy_offs = RKVPSS_SCALE2_VY_OFFS, + .vc_offs = RKVPSS_SCALE2_VC_OFFS, + .hy_size = RKVPSS_SCALE2_HY_SIZE, + .hc_size = RKVPSS_SCALE2_HC_SIZE, + .hy_offs_mi = RKVPSS_SCALE2_HY_OFFS_MI, + .hc_offs_mi = RKVPSS_SCALE2_HC_OFFS_MI, + .in_crop_offs = RKVPSS_SCALE2_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS_SCALE2_CTRL_SHD, + .src_size_shd = RKVPSS_SCALE2_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS_SCALE2_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS_SCALE2_HY_FAC_SHD, + .hc_fac_shd = RKVPSS_SCALE2_HC_FAC_SHD, + .vy_fac_shd = RKVPSS_SCALE2_VY_FAC_SHD, + .vc_fac_shd = RKVPSS_SCALE2_VC_FAC_SHD, + .hy_offs_shd = RKVPSS_SCALE2_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS_SCALE2_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS_SCALE2_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS_SCALE2_VC_OFFS_SHD, + .hy_size_shd = RKVPSS_SCALE2_HY_SIZE_SHD, + .hc_size_shd = RKVPSS_SCALE2_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS_SCALE2_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS_SCALE2_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS_SCALE2_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN2_WR_CTRL, + .stride = RKVPSS_MI_CHN2_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN2_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN2_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN2_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN2_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN2_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN2_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN2_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN2_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN2_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN2_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN2_WR_CB_BASE_SHD, + }, +}; + +static struct stream_config scl3_config = { + .fmts = scl2_3_fmts, + .fmt_size = ARRAY_SIZE(scl2_3_fmts), + .frame_end_id = RKVPSS_MI_CHN3_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_3_H_OFFS, + .v_offs = RKVPSS_CROP1_3_V_OFFS, + .h_size = RKVPSS_CROP1_3_H_SIZE, + .v_size = RKVPSS_CROP1_3_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_3_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_3_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_3_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_3_V_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS_SCALE3_CTRL, + .update = RKVPSS_SCALE3_UPDATE, + .src_size = RKVPSS_SCALE3_SRC_SIZE, + .dst_size = RKVPSS_SCALE3_DST_SIZE, + .hy_fac = RKVPSS_SCALE3_HY_FAC, + .hc_fac = RKVPSS_SCALE3_HC_FAC, + .vy_fac = RKVPSS_SCALE3_VY_FAC, + .vc_fac = RKVPSS_SCALE3_VC_FAC, + .hy_offs = RKVPSS_SCALE3_HY_OFFS, + .hc_offs = RKVPSS_SCALE3_HC_OFFS, + .vy_offs = RKVPSS_SCALE3_VY_OFFS, + .vc_offs = RKVPSS_SCALE3_VC_OFFS, + .hy_size = RKVPSS_SCALE3_HY_SIZE, + .hc_size = RKVPSS_SCALE3_HC_SIZE, + .hy_offs_mi = RKVPSS_SCALE3_HY_OFFS_MI, + .hc_offs_mi = RKVPSS_SCALE3_HC_OFFS_MI, + .in_crop_offs = RKVPSS_SCALE3_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS_SCALE3_CTRL_SHD, + .src_size_shd = RKVPSS_SCALE3_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS_SCALE3_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS_SCALE3_HY_FAC_SHD, + .hc_fac_shd = RKVPSS_SCALE3_HC_FAC_SHD, + .vy_fac_shd = RKVPSS_SCALE3_VY_FAC_SHD, + .vc_fac_shd = RKVPSS_SCALE3_VC_FAC_SHD, + .hy_offs_shd = RKVPSS_SCALE3_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS_SCALE3_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS_SCALE3_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS_SCALE3_VC_OFFS_SHD, + .hy_size_shd = RKVPSS_SCALE3_HY_SIZE_SHD, + .hc_size_shd = RKVPSS_SCALE3_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS_SCALE3_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS_SCALE3_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS_SCALE3_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN3_WR_CTRL, + .stride = RKVPSS_MI_CHN3_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN3_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN3_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN3_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN3_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN3_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN3_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN3_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN3_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN3_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN3_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN3_WR_CB_BASE_SHD, + }, +}; + +static void calc_unite_scl_params(struct rkvpss_stream *stream) +{ + struct rkvpss_online_unite_params *params = &stream->unite_params; + u32 right_scl_need_size_y, right_scl_need_size_c; + u32 left_in_used_size_y, left_in_used_size_c; + u32 right_fst_position_y, right_fst_position_c; + u32 right_y_crop_total; + u32 right_c_crop_total; + + params->y_w_fac = (stream->crop.width - 1) * 4096 / + (stream->out_fmt.width - 1); + params->c_w_fac = (stream->crop.width / 2 - 1) * 4096 / + (stream->out_fmt.width / 2 - 1); + params->y_h_fac = (stream->crop.height - 1) * 4096 / + (stream->out_fmt.height - 1); + params->c_h_fac = (stream->crop.height - 1) * 4096 / + (stream->out_fmt.height - 1); + + right_fst_position_y = stream->out_fmt.width / 2 * + params->y_w_fac; + right_fst_position_c = stream->out_fmt.width / 2 / 2 * + params->c_w_fac; + + left_in_used_size_y = right_fst_position_y >> 12; + left_in_used_size_c = (right_fst_position_c >> 12) * 2; + + params->y_w_phase = right_fst_position_y & 0xfff; + params->c_w_phase = right_fst_position_c & 0xfff; + + right_scl_need_size_y = stream->crop.width - + left_in_used_size_y; + params->right_scl_need_size_y = right_scl_need_size_y; + right_scl_need_size_c = stream->crop.width - + left_in_used_size_c; + params->right_scl_need_size_c = right_scl_need_size_c; + + if (stream->id == 0 && stream->crop.width != stream->out_fmt.width) { + right_y_crop_total = stream->crop.width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL - + right_scl_need_size_y - 3; + right_c_crop_total = stream->crop.width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL - + right_scl_need_size_c - 6; + } else { + right_y_crop_total = stream->crop.width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL - + right_scl_need_size_y; + right_c_crop_total = stream->crop.width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL - + right_scl_need_size_c; + } + + params->quad_crop_w = ALIGN_DOWN(min(right_y_crop_total, right_c_crop_total), 2); + + params->scl_in_crop_w_y = right_y_crop_total - params->quad_crop_w; + params->scl_in_crop_w_c = right_c_crop_total - params->quad_crop_w; + + if (rkvpss_debug >= 2) { + v4l2_info(&stream->dev->v4l2_dev, + "%s ch:%d y_w_fac:%u c_w_fac:%u y_h_fac:%u c_h_fac:%u\n", + __func__, stream->id, params->y_w_fac, + params->c_w_fac, params->y_h_fac, params->c_h_fac); + v4l2_info(&stream->dev->v4l2_dev, + "\t%s y_w_phase:%u c_w_phase:%u quad_crop_w:%u scl_in_crop_w_y:%u scl_in_crop_w_c:%u\n", + __func__, params->y_w_phase, params->c_w_phase, + params->quad_crop_w, + params->scl_in_crop_w_y, params->scl_in_crop_w_c); + v4l2_info(&stream->dev->v4l2_dev, + "\t%s right_scl_need_size_y:%u right_scl_need_size_c:%u\n", + __func__, params->right_scl_need_size_y, + params->right_scl_need_size_c); + } +} + +int rkvpss_stream_buf_cnt_v1(struct rkvpss_stream *stream) +{ + unsigned long lock_flags = 0; + struct rkvpss_buffer *buf, *tmp; + int cnt = 0; + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_for_each_entry_safe(buf, tmp, &stream->buf_queue, queue) + cnt++; + if (stream->curr_buf) + cnt++; + if (stream->next_buf && stream->next_buf != stream->curr_buf) + cnt++; + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + return cnt; +} + +static void stream_frame_start(struct rkvpss_stream *stream, u32 irq) +{ + struct rkvpss_device *dev = stream->dev; + + if (stream->is_crop_upd) { + if (dev->unite_mode) + calc_unite_scl_params(stream); + rkvpss_stream_scale(stream, true, !irq); + rkvpss_stream_crop(stream, true, !irq); + } + if (!irq && !stream->curr_buf && + !stream->dev->hw_dev->is_single) + stream->ops->update_mi(stream); +} + +static void scl_force_update(struct rkvpss_stream *stream) +{ + u32 val; + + switch (stream->id) { + case RKVPSS_OUTPUT_CH0: + val = RKVPSS_MI_CHN0_FORCE_UPD; + break; + case RKVPSS_OUTPUT_CH1: + val = RKVPSS_MI_CHN1_FORCE_UPD; + break; + case RKVPSS_OUTPUT_CH2: + val = RKVPSS_MI_CHN2_FORCE_UPD; + break; + case RKVPSS_OUTPUT_CH3: + val = RKVPSS_MI_CHN3_FORCE_UPD; + break; + default: + return; + } + /* need update two for online2 mode */ + rkvpss_unite_write(stream->dev, RKVPSS_MI_WR_INIT, val); + rkvpss_unite_write(stream->dev, RKVPSS_MI_WR_INIT, val); +} + +static void scl_update_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct rkvpss_buffer *buf = NULL; + unsigned long lock_flags = 0; + u32 y_base, uv_base; + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (!list_empty(&stream->buf_queue) && !stream->curr_buf) { + buf = list_first_entry(&stream->buf_queue, struct rkvpss_buffer, queue); + list_del(&buf->queue); + stream->curr_buf = buf; + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + if (buf) { + y_base = buf->dma[0]; + uv_base = buf->dma[1]; + rkvpss_idx_write(dev, stream->config->mi.y_base, y_base, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, stream->config->mi.uv_base, uv_base, VPSS_UNITE_LEFT); + if (dev->unite_mode) { + y_base = buf->dma[0] + stream->out_fmt.width / 2; + uv_base = buf->dma[1] + stream->out_fmt.width / 2; + rkvpss_idx_write(dev, stream->config->mi.y_base, y_base, + VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, stream->config->mi.uv_base, uv_base, + VPSS_UNITE_RIGHT); + } + + scl_force_update(stream); + if (stream->is_pause) { + stream->is_pause = false; + stream->ops->enable_mi(stream); + } + } else if (!stream->is_pause) { + stream->is_pause = true; + stream->ops->disable_mi(stream); + } + + v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d unite_index:%d Y:0x%x UV:0x%x | Y_SHD:0x%x\n", + __func__, stream->id, dev->unite_index, + rkvpss_idx_read(dev, stream->config->mi.y_base, dev->unite_index), + rkvpss_idx_read(dev, stream->config->mi.uv_base, dev->unite_index), + rkvpss_hw_read(dev->hw_dev, stream->config->mi.y_shd)); +} + +static void scl_config_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct capture_fmt *fmt = &stream->out_cap_fmt; + struct v4l2_pix_format_mplane *out_fmt = &stream->out_fmt; + u32 reg, val, mask; + + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "%s stream:%d\n", __func__, stream->id); + + val = out_fmt->plane_fmt[0].bytesperline; + reg = stream->config->mi.stride; + rkvpss_unite_write(dev, reg, val); + rkvpss_unite_write(dev, reg, val); + + switch (fmt->fourcc) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + val = out_fmt->plane_fmt[0].bytesperline / 2; + break; + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_XRGB32: + val = out_fmt->plane_fmt[0].bytesperline / 4; + break; + default: + val = out_fmt->plane_fmt[0].bytesperline; + break; + } + + val = val * out_fmt->height; + reg = stream->config->mi.y_pic_size; + rkvpss_unite_write(dev, reg, val); + + val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height; + reg = stream->config->mi.y_size; + rkvpss_unite_write(dev, reg, val); + + val = out_fmt->plane_fmt[1].sizeimage; + reg = stream->config->mi.uv_size; + rkvpss_unite_write(dev, reg, val); + + reg = stream->config->mi.y_offs_cnt; + rkvpss_unite_write(dev, reg, 0); + reg = stream->config->mi.uv_offs_cnt; + rkvpss_unite_write(dev, reg, 0); + + val = fmt->wr_fmt | fmt->output_fmt | fmt->swap | + RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; + reg = stream->config->mi.ctrl; + rkvpss_unite_write(dev, reg, val); + + switch (fmt->fourcc) { + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_VYUY: + mask = RKVPSS_MI_WR_UV_SWAP; + val = RKVPSS_MI_WR_UV_SWAP; + rkvpss_hw_set_bits(dev->hw_dev, RKVPSS_MI_WR_CTRL, mask, val); + break; + case V4L2_PIX_FMT_TILE420: + case V4L2_PIX_FMT_TILE422: + mask = RKVPSS_MI_WR_TILE_SEL(3); + val = RKVPSS_MI_WR_TILE_SEL(stream->id + 1); + rkvpss_hw_set_bits(dev->hw_dev, RKVPSS_MI_WR_CTRL, mask, val); + break; + default: + break; + } + + stream->is_mf_upd = true; + rkvpss_frame_end(stream); +} + +static void scl_enable_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + u32 val, mask; + + switch (stream->id) { + case RKVPSS_OUTPUT_CH0: + val = RKVPSS_ISP2VPSS_CHN0_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN0_SEL(3); + break; + case RKVPSS_OUTPUT_CH1: + val = RKVPSS_ISP2VPSS_CHN1_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN1_SEL(3); + break; + case RKVPSS_OUTPUT_CH2: + val = RKVPSS_ISP2VPSS_CHN2_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN2_SEL(3); + break; + case RKVPSS_OUTPUT_CH3: + val = RKVPSS_ISP2VPSS_CHN3_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN3_SEL(3); + break; + default: + return; + } + val |= RKVPSS_ISP2VPSS_ONLINE2; + if (!dev->hw_dev->is_ofl_cmsc) + val |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN; + rkvpss_unite_set_bits(dev, RKVPSS_VPSS_ONLINE, mask, val); + val = RKVPSS_ONLINE2_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD; + rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val); + v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d\n", __func__, + stream->id); +} + +static void scl_disable_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + u32 val; + + switch (stream->id) { + case RKVPSS_OUTPUT_CH0: + val = RKVPSS_ISP2VPSS_CHN0_SEL(3); + break; + case RKVPSS_OUTPUT_CH1: + val = RKVPSS_ISP2VPSS_CHN1_SEL(3); + break; + case RKVPSS_OUTPUT_CH2: + val = RKVPSS_ISP2VPSS_CHN2_SEL(3); + break; + case RKVPSS_OUTPUT_CH3: + val = RKVPSS_ISP2VPSS_CHN3_SEL(3); + break; + default: + return; + } + + rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_ONLINE, val); + val = RKVPSS_ONLINE2_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD; + rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val); + v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d\n", __func__, + stream->id); +} + +static struct streams_ops scl_stream_ops = { + .config_mi = scl_config_mi, + .enable_mi = scl_enable_mi, + .disable_mi = scl_disable_mi, + .update_mi = scl_update_mi, + .frame_start = stream_frame_start, +}; + +static void rkvpss_buf_done_task(unsigned long arg) +{ + struct rkvpss_stream *stream = (struct rkvpss_stream *)arg; + struct rkvpss_vdev_node *node = &stream->vnode; + struct rkvpss_buffer *buf = NULL; + unsigned long lock_flags = 0; + LIST_HEAD(local_list); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_replace_init(&stream->buf_done_list, &local_list); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + while (!list_empty(&local_list)) { + buf = list_first_entry(&local_list, struct rkvpss_buffer, queue); + list_del(&buf->queue); + v4l2_dbg(2, rkvpss_debug, &stream->dev->v4l2_dev, + "%s stream:%d index:%d seq:%d buf:0x%x done\n", + node->vdev.name, stream->id, buf->vb.vb2_buf.index, + buf->vb.sequence, buf->dma[0]); + vb2_buffer_done(&buf->vb.vb2_buf, + stream->streaming ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR); + } +} + +static void rkvpss_stream_buf_done(struct rkvpss_stream *stream, + struct rkvpss_buffer *buf) +{ + unsigned long lock_flags = 0; + + if (!stream || !buf) + return; + + v4l2_dbg(3, rkvpss_debug, &stream->dev->v4l2_dev, + "stream:%d\n", stream->id); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_add_tail(&buf->queue, &stream->buf_done_list); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + tasklet_schedule(&stream->buf_done_tasklet); +} + +static void rkvpss_fill_frame_info(struct rkvpss_frame_info *dst_info, + struct rkisp_vpss_frame_info *src_info) +{ + dst_info->timestamp = src_info->timestamp; + + dst_info->seq = src_info->seq; + dst_info->hdr = src_info->hdr; + dst_info->rolling_shutter_skew = src_info->rolling_shutter_skew; + + dst_info->sensor_exposure_time = src_info->sensor_exposure_time; + dst_info->sensor_analog_gain = src_info->sensor_analog_gain; + dst_info->sensor_digital_gain = src_info->sensor_digital_gain; + dst_info->isp_digital_gain = src_info->isp_digital_gain; + + dst_info->sensor_exposure_time_m = src_info->sensor_exposure_time_m; + dst_info->sensor_analog_gain_m = src_info->sensor_analog_gain_m; + dst_info->sensor_digital_gain_m = src_info->sensor_digital_gain_m; + dst_info->isp_digital_gain_m = src_info->isp_digital_gain_m; + + dst_info->sensor_exposure_time_l = src_info->sensor_exposure_time_l; + dst_info->sensor_analog_gain_l = src_info->sensor_analog_gain_l; + dst_info->sensor_digital_gain_l = src_info->sensor_digital_gain_l; + dst_info->isp_digital_gain_l = src_info->isp_digital_gain_l; +} + +static void rkvpss_frame_end(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct rkvpss_subdev *sdev = &dev->vpss_sdev; + struct rkvpss_buffer *buf = NULL; + unsigned long lock_flags = 0; + + v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, + "%s stream:%d\n", __func__, stream->id); + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (stream->curr_buf) { + buf = stream->curr_buf; + stream->curr_buf = NULL; + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + if (buf) { + struct vb2_buffer *vb2_buf = &buf->vb.vb2_buf; + struct capture_fmt *fmt = &stream->out_cap_fmt; + u64 ns = sdev->frame_timestamp; + int i; + + for (i = 0; i < fmt->mplanes; i++) { + u32 payload_size = stream->out_fmt.plane_fmt[i].sizeimage; + + vb2_set_plane_payload(vb2_buf, i, payload_size); + + if (stream->is_attach_info && i == fmt->mplanes - 1) { + struct rkvpss_frame_info *dst_info = buf->vaddr[i] + payload_size; + struct rkisp_vpss_frame_info *src_info = &dev->frame_info; + + rkvpss_fill_frame_info(dst_info, src_info); + } + } + if (!ns) + ns = ktime_get_ns(); + buf->vb.vb2_buf.timestamp = ns; + buf->vb.sequence = sdev->frame_seq; + + ns = ktime_get_ns(); + stream->dbg.frameloss += buf->vb.sequence - stream->dbg.id - 1; + stream->dbg.id = buf->vb.sequence; + stream->dbg.delay = ns - buf->vb.vb2_buf.timestamp; + stream->dbg.interval = ns - stream->dbg.timestamp; + stream->dbg.timestamp = ns; + rkvpss_stream_buf_done(stream, buf); + } + + rkvpss_stream_mf(stream); + stream->ops->update_mi(stream); +} + +static int rkvpss_queue_setup(struct vb2_queue *queue, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_ctxs[]) +{ + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_device *dev = stream->dev; + const struct v4l2_pix_format_mplane *pixm = NULL; + const struct capture_fmt *cap_fmt = NULL; + u32 i; + + pixm = &stream->out_fmt; + if (!pixm->width || !pixm->height) + return -EINVAL; + cap_fmt = &stream->out_cap_fmt; + *num_planes = cap_fmt->mplanes; + + for (i = 0; i < cap_fmt->mplanes; i++) { + const struct v4l2_plane_pix_format *plane_fmt; + + plane_fmt = &pixm->plane_fmt[i]; + sizes[i] = plane_fmt->sizeimage / pixm->height * ALIGN(pixm->height, 16); + + if (stream->is_attach_info && i == cap_fmt->mplanes - 1) + sizes[i] += sizeof(struct rkvpss_frame_info); + } + + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s stream:%d count %d, size %d\n", + v4l2_type_names[queue->type], + stream->id, *num_buffers, sizes[0]); + + return 0; +} + +static void rkvpss_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkvpss_buffer *vpssbuf = to_rkvpss_buffer(vbuf); + struct vb2_queue *queue = vb->vb2_queue; + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_device *dev = stream->dev; + struct v4l2_pix_format_mplane *pixm = &stream->out_fmt; + struct capture_fmt *cap_fmt = &stream->out_cap_fmt; + unsigned long lock_flags = 0; + struct sg_table *sgt; + u32 height, size; + int i; + + for (i = 0; i < cap_fmt->mplanes; i++) { + sgt = vb2_dma_sg_plane_desc(vb, i); + vpssbuf->dma[i] = sg_dma_address(sgt->sgl); + + vpssbuf->vaddr[i] = vb2_plane_vaddr(vb, i); + } + /* + * NOTE: plane_fmt[0].sizeimage is total size of all planes for single + * memory plane formats, so calculate the size explicitly. + */ + if (cap_fmt->mplanes == 1) { + for (i = 0; i < cap_fmt->cplanes - 1; i++) { + height = pixm->height; + size = (i == 0) ? + pixm->plane_fmt[i].bytesperline * height : + pixm->plane_fmt[i].sizeimage; + vpssbuf->dma[i + 1] = vpssbuf->dma[i] + size; + } + } + + v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, + "%s stream:%d index:%d buf:0x%x\n", __func__, + stream->id, vb->index, vpssbuf->dma[0]); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_add_tail(&vpssbuf->queue, &stream->buf_queue); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + if (dev->hw_dev->is_single && + stream->streaming && !stream->curr_buf) + stream->ops->update_mi(stream); +} + +static void destroy_buf_queue(struct rkvpss_stream *stream, + enum vb2_buffer_state state) +{ + unsigned long lock_flags = 0; + struct rkvpss_buffer *buf; + LIST_HEAD(queue_local_list); + LIST_HEAD(done_local_list); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (stream->curr_buf) { + list_add_tail(&stream->curr_buf->queue, &stream->buf_queue); + stream->curr_buf = NULL; + } + list_replace_init(&stream->buf_queue, &queue_local_list); + list_replace_init(&stream->buf_done_list, &done_local_list); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + while (!list_empty(&queue_local_list)) { + buf = list_first_entry(&queue_local_list, struct rkvpss_buffer, queue); + list_del(&buf->queue); + buf->vb.vb2_buf.synced = false; + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + while (!list_empty(&done_local_list)) { + buf = list_first_entry(&done_local_list, struct rkvpss_buffer, queue); + list_del(&buf->queue); + buf->vb.vb2_buf.synced = false; + vb2_buffer_done(&buf->vb.vb2_buf, state); + } +} + +static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync) +{ + struct rkvpss_device *dev = stream->dev; + struct v4l2_rect *crop = &stream->crop; + u32 reg_ctrl = stream->config->crop.ctrl; + u32 reg_upd = stream->config->crop.update; + u32 reg_h_offs = stream->config->crop.h_offs; + u32 reg_v_offs = stream->config->crop.v_offs; + u32 reg_h_size = stream->config->crop.h_size; + u32 reg_v_size = stream->config->crop.v_size; + u32 val; + + val = RKVPSS_CROP_CHN_EN(stream->id); + if (!dev->unite_mode) { + if (on) { + rkvpss_unite_set_bits(dev, reg_ctrl, 0, val); + rkvpss_unite_write(dev, reg_h_offs, crop->left); + rkvpss_unite_write(dev, reg_v_offs, crop->top); + rkvpss_unite_write(dev, reg_h_size, crop->width); + rkvpss_unite_write(dev, reg_v_size, crop->height); + + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "crop left:%d top:%d w:%d h:%d\n", + crop->left, crop->top, crop->width, crop->height); + } else { + rkvpss_unite_clear_bits(dev, reg_ctrl, val); + } + if (sync) { + val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id); + val |= RKVPSS_CROP_GEN_UPD; + rkvpss_unite_write(dev, reg_upd, val); + rkvpss_unite_write(dev, reg_upd, val); + } + } else { + if (on) { + if (crop->left + crop->width / 2 != dev->vpss_sdev.in_fmt.width / 2) { + v4l2_err(&dev->v4l2_dev, + "unite need centered crop left:%d top:%d w:%d h:%d\n", + crop->left, crop->top, crop->width, crop->height); + return -EINVAL; + } + /* unite left */ + rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, reg_h_offs, crop->left, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, reg_v_offs, crop->top, VPSS_UNITE_LEFT); + /* if no scale left don't enlarge */ + if (crop->width == stream->out_fmt.width) + rkvpss_idx_write(dev, reg_h_size, crop->width / 2, + VPSS_UNITE_LEFT); + else + rkvpss_idx_write(dev, reg_h_size, crop->width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, reg_v_size, crop->height, VPSS_UNITE_LEFT); + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "left crop left:%d top:%d w:%d h:%d\n", + rkvpss_idx_read(dev, reg_h_offs, VPSS_UNITE_LEFT), + rkvpss_idx_read(dev, reg_v_offs, VPSS_UNITE_LEFT), + rkvpss_idx_read(dev, reg_h_size, VPSS_UNITE_LEFT), + rkvpss_idx_read(dev, reg_v_size, VPSS_UNITE_LEFT)); + + /* unite right */ + rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, reg_h_offs, stream->unite_params.quad_crop_w, + VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, reg_v_offs, crop->top, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, reg_h_size, crop->width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL - + stream->unite_params.quad_crop_w, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, reg_v_size, crop->height, VPSS_UNITE_RIGHT); + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "right crop left:%d top:%d w:%d h:%d\n", + rkvpss_idx_read(dev, reg_h_offs, VPSS_UNITE_RIGHT), + rkvpss_idx_read(dev, reg_v_offs, VPSS_UNITE_RIGHT), + rkvpss_idx_read(dev, reg_h_size, VPSS_UNITE_RIGHT), + rkvpss_idx_read(dev, reg_v_size, VPSS_UNITE_RIGHT)); + } else { + rkvpss_idx_clear_bits(dev, reg_ctrl, val, VPSS_UNITE_LEFT); + rkvpss_idx_clear_bits(dev, reg_ctrl, val, VPSS_UNITE_RIGHT); + } + if (sync) { + val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id); + val |= RKVPSS_CROP_GEN_UPD; + rkvpss_idx_write(dev, reg_upd, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, reg_upd, val, VPSS_UNITE_RIGHT); + } + } + stream->is_crop_upd = false; + return 0; +} + +static void poly_phase_scale(struct rkvpss_stream *stream, bool on, bool sync) +{ + struct rkvpss_device *dev = stream->dev; + u32 out_w = stream->out_fmt.width; + u32 out_h = stream->out_fmt.height; + u32 in_w = stream->crop.width; + u32 in_h = stream->crop.height; + u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac; + u32 i, j, idx, ratio, val, in_div, out_div, factor; + bool dering_en = false, yuv420_in = false, yuv422_to_420 = false; + + if (!on) { + rkvpss_unite_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0); + if (dev->unite_mode) { + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0, VPSS_UNITE_RIGHT); + } + return; + } + + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d in_w:%d in_h:%d out_w:%d out_h:%d\n", __func__, + stream->id, in_w, in_h, out_w, out_h); + + /* constraints */ + if (((out_w >= in_w) && (in_w * 8 < out_w)) || + ((in_w > out_w) && (out_w * 8 < in_w)) || + ((out_h >= in_h) && (in_h * 6 < out_h)) || + ((in_h > out_h) && (out_h * 6 < in_h))) { + v4l2_err(&dev->v4l2_dev, "stream:%d scale size error\n", stream->id); + return; + } + + /*config scl clk gate*/ + if (in_w == out_w && in_h == out_h) + rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS); + else + rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS, + RKVPSS_SCL0_CKG_DIS); + + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + if (dev->unite_mode) { + /* unite left */ + if (in_w == out_w) + val = (in_w / 2 - 1) | ((in_h - 1) << 16); + else + val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL - 1) | + ((in_h - 1) << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val, VPSS_UNITE_LEFT); + + /* unite right */ + val = (ALIGN(stream->unite_params.right_scl_need_size_y + 3, 2) - 1) | + ((in_h - 1) << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val, VPSS_UNITE_RIGHT); + val = (ALIGN(stream->unite_params.right_scl_need_size_c + 6, 2) - 1) | + ((in_h - 1) << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val, VPSS_UNITE_RIGHT); + + val = (out_w / 2 - 1) | ((out_h - 1) << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_DST_SIZE, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_DST_SIZE, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_DST_SIZE, val, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_DST_SIZE, val, VPSS_UNITE_RIGHT); + } else { + val = (in_w - 1) | ((in_h - 1) << 16); + rkvpss_unite_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val); + val = (out_w - 1) | ((out_h - 1) << 16); + rkvpss_unite_write(dev, RKVPSS_ZME_Y_DST_SIZE, val); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_DST_SIZE, val); + } + + ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; + if (dering_en) { + ctrl |= RKVPSS_ZME_DERING_EN; + rkvpss_unite_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410); + + if (dev->unite_mode) { + rkvpss_idx_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410, + VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410, + VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410, + VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410, + VPSS_UNITE_RIGHT); + } + } + + if (in_w != out_w) { + if (in_w > out_w) { + factor = 4096; + ctrl |= RKVPSS_ZME_XSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_XSU_EN; + } + y_xscl_fac = (in_w - 1) * factor / (out_w - 1); + uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1); + + ratio = y_xscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j], + rkvpss_zme_tap8_coe[idx][i][j + 1]); + rkvpss_unite_write(dev, + RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, + val); + rkvpss_unite_write(dev, + RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, + val); + + if (dev->unite_mode) { + rkvpss_idx_write(dev, + RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, + RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, + RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, + RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_RIGHT); + } + } + } + } else { + y_xscl_fac = 0; + uv_xscl_fac = 0; + } + + if (dev->unite_mode) { + /* unite left */ + rkvpss_idx_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac, VPSS_UNITE_LEFT); + + /* unite right */ + val = y_xscl_fac | (stream->unite_params.y_w_phase << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, val, VPSS_UNITE_RIGHT); + val = uv_xscl_fac | (stream->unite_params.c_w_phase << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, val, VPSS_UNITE_RIGHT); + } else { + rkvpss_unite_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); + } + + if (in_h != out_h) { + if (in_h > out_h) { + factor = 4096; + ctrl |= RKVPSS_ZME_YSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_YSU_EN; + } + y_yscl_fac = (in_h - 1) * factor / (out_h - 1); + uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1); + + ratio = y_yscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j], + rkvpss_zme_tap6_coe[idx][i][j + 1]); + rkvpss_unite_write(dev, + RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, + val); + rkvpss_unite_write(dev, + RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, + val); + + if (dev->unite_mode) { + rkvpss_idx_write(dev, + RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, + RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, + RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, + RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_RIGHT); + } + } + } + } else { + y_yscl_fac = 0; + uv_yscl_fac = 0; + } + + if (dev->unite_mode) { + /* unite left */ + rkvpss_idx_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl, VPSS_UNITE_LEFT); + + /* unite right */ + rkvpss_idx_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl, VPSS_UNITE_RIGHT); + } else { + rkvpss_unite_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac); + rkvpss_unite_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl); + } + + if (dev->unite_mode) { + /* unite left */ + val = out_w / 2; + rkvpss_idx_write(dev, RKVPSS_ZME_H_SIZE, (val << 16) | val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, 0, VPSS_UNITE_LEFT); + + /* unite right */ + rkvpss_idx_write(dev, RKVPSS_ZME_H_SIZE, (val << 16) | val, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, 0, VPSS_UNITE_RIGHT); + val = out_w / 2 - ALIGN_DOWN(out_w / 2, 16); + rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, (3 << 20) | (3 << 16) | + (stream->unite_params.scl_in_crop_w_y << 8) | + (stream->unite_params.scl_in_crop_w_c << 12) | + (val << 4) | val, VPSS_UNITE_RIGHT); + } + + ctrl = RKVPSS_ZME_GATING_EN; + if (yuv420_in) + ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; + if (yuv422_to_420) + ctrl |= RKVPSS_ZME_422TO420_EN; + if (dev->unite_mode) { + /* unite left */ + ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_8K_EN; + rkvpss_idx_write(dev, RKVPSS_ZME_CTRL, ctrl, VPSS_UNITE_LEFT); + + /* unite left */ + ctrl |= RKVPSS_ZME_IN_CLIP_EN; + rkvpss_idx_write(dev, RKVPSS_ZME_CTRL, ctrl, VPSS_UNITE_RIGHT); + } else { + rkvpss_unite_write(dev, RKVPSS_ZME_CTRL, ctrl); + } + + val = RKVPSS_ZME_GEN_UPD; + if (sync) + val |= RKVPSS_ZME_FORCE_UPD; + rkvpss_unite_write(dev, RKVPSS_ZME_UPDATE, val); + if (dev->unite_mode) { + rkvpss_idx_write(dev, RKVPSS_ZME_UPDATE, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UPDATE, val, VPSS_UNITE_RIGHT); + } +} + +static void bilinear_scale(struct rkvpss_stream *stream, bool on, bool sync) +{ + struct rkvpss_device *dev = stream->dev; + u32 out_w = stream->out_fmt.width; + u32 out_h = stream->out_fmt.height; + u32 in_w = stream->crop.width; + u32 in_h = stream->crop.height; + u32 in_div, out_div; + u32 reg, val, ctrl = 0, clk_mask = 0; + bool yuv420_in = false, yuv422_to_420 = false; + + if (!on) { + reg = stream->config->scale.ctrl; + rkvpss_unite_write(dev, reg, 0); + return; + } + + /*config scl clk gate*/ + switch (stream->id) { + case RKVPSS_OUTPUT_CH1: + clk_mask = RKVPSS_SCL1_CKG_DIS; + break; + case RKVPSS_OUTPUT_CH2: + clk_mask = RKVPSS_SCL2_CKG_DIS; + break; + case RKVPSS_OUTPUT_CH3: + clk_mask = RKVPSS_SCL3_CKG_DIS; + break; + default: + return; + } + if (in_w == out_w && in_h == out_h) + rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_CLK_GATE, clk_mask); + else + rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CLK_GATE, clk_mask, clk_mask); + + if (!dev->unite_mode) { + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + reg = stream->config->scale.hy_offs; + rkvpss_unite_write(dev, reg, 0); + reg = stream->config->scale.hc_offs; + rkvpss_unite_write(dev, reg, 0); + reg = stream->config->scale.vy_offs; + rkvpss_unite_write(dev, reg, 0); + reg = stream->config->scale.vc_offs; + rkvpss_unite_write(dev, reg, 0); + + val = in_w | (in_h << 16); + reg = stream->config->scale.src_size; + rkvpss_unite_write(dev, reg, val); + + /* ch2 ch3 scale out width exceed 1920 */ + if ((stream->id >= RKVPSS_OUTPUT_CH2) && + (in_w != out_w) && (out_w > 1920)) + v4l2_err(&dev->v4l2_dev, + "stream:%d scale out width exceed 1920\n", + stream->id); + + val = out_w | (out_h << 16); + reg = stream->config->scale.dst_size; + rkvpss_unite_write(dev, reg, val); + + if (in_w != out_w || !sync) { + val = (in_w - 1) * 4096 / (out_w - 1); + reg = stream->config->scale.hy_fac; + rkvpss_unite_write(dev, reg, val); + + val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); + reg = stream->config->scale.hc_fac; + rkvpss_unite_write(dev, reg, val); + + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + if (in_h != out_h || !sync) { + val = (in_h - 1) * 4096 / (out_h - 1); + reg = stream->config->scale.vy_fac; + rkvpss_unite_write(dev, reg, val); + + val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); + reg = stream->config->scale.vc_fac; + rkvpss_unite_write(dev, reg, val); + + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + + reg = stream->config->scale.ctrl; + rkvpss_unite_write(dev, reg, ctrl); + + val = RKVPSS_SCL_GEN_UPD; + if (sync) + val |= RKVPSS_SCL_FORCE_UPD; + reg = stream->config->scale.update; + rkvpss_unite_write(dev, reg, val); + } else { + /* unite left */ + reg = stream->config->scale.in_crop_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + + reg = stream->config->scale.hy_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + reg = stream->config->scale.hc_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + reg = stream->config->scale.vy_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + reg = stream->config->scale.vc_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + + reg = stream->config->scale.hy_offs_mi; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + reg = stream->config->scale.hc_offs_mi; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + + if (in_w == out_w) + val = (in_w / 2) | (in_h << 16); + else + val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL) | (in_h << 16); + reg = stream->config->scale.src_size; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); + + /* ch2 ch3 scale out width exceed 1920 */ + if ((stream->id >= RKVPSS_OUTPUT_CH2) && + (in_w != out_w) && (out_w / 2 > 1920)) + v4l2_err(&dev->v4l2_dev, + "stream:%d scale out width exceed 1920\n", + stream->id); + + val = (out_w / 2) | (out_h << 16); + reg = stream->config->scale.dst_size; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); + + ctrl |= RKVPSS_SCL_CLIP_EN; + + if (in_w != out_w || !sync) { + reg = stream->config->scale.hy_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.y_w_fac, VPSS_UNITE_LEFT); + reg = stream->config->scale.hc_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.c_w_fac, VPSS_UNITE_LEFT); + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + + if (in_h != out_h || !sync) { + reg = stream->config->scale.vy_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.y_h_fac, VPSS_UNITE_LEFT); + reg = stream->config->scale.vc_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.c_h_fac, VPSS_UNITE_LEFT); + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + + reg = stream->config->scale.ctrl; + rkvpss_idx_write(dev, reg, ctrl, VPSS_UNITE_LEFT); + + val = RKVPSS_SCL_GEN_UPD; + if (sync) + val |= RKVPSS_SCL_FORCE_UPD; + reg = stream->config->scale.update; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); + + /* unite right */ + ctrl = 0; + val = stream->unite_params.scl_in_crop_w_y | + (stream->unite_params.scl_in_crop_w_c << 4); + reg = stream->config->scale.in_crop_offs; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + + val = stream->unite_params.y_w_phase; + reg = stream->config->scale.hy_offs; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + val = stream->unite_params.c_w_phase; + reg = stream->config->scale.hc_offs; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + reg = stream->config->scale.vy_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_RIGHT); + reg = stream->config->scale.vc_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_RIGHT); + + val = out_w / 2 - ALIGN_DOWN(out_w / 2, 16); + reg = stream->config->scale.hy_offs_mi; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + reg = stream->config->scale.hc_offs_mi; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + + val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL) | (in_h << 16); + reg = stream->config->scale.src_size; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + + /* ch2 ch3 scale out width exceed 1920 */ + if ((stream->id >= RKVPSS_OUTPUT_CH2) && + (in_w != out_w) && (out_w / 2 > 1920)) + v4l2_err(&dev->v4l2_dev, + "stream:%d scale out width exceed 1920\n", + stream->id); + + val = (out_w / 2) | (out_h << 16); + reg = stream->config->scale.dst_size; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + + ctrl |= RKVPSS_SCL_CLIP_EN | RKVPSS_SCL_IN_CLIP_EN; + + if (in_w != out_w || !sync) { + reg = stream->config->scale.hy_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.y_w_fac, VPSS_UNITE_RIGHT); + reg = stream->config->scale.hc_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.c_w_fac, VPSS_UNITE_RIGHT); + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + + if (in_h != out_h || !sync) { + reg = stream->config->scale.vy_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.y_h_fac, VPSS_UNITE_RIGHT); + reg = stream->config->scale.vc_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.c_h_fac, VPSS_UNITE_RIGHT); + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + + reg = stream->config->scale.ctrl; + rkvpss_idx_write(dev, reg, ctrl, VPSS_UNITE_RIGHT); + + val = RKVPSS_SCL_GEN_UPD; + if (sync) + val |= RKVPSS_SCL_FORCE_UPD; + reg = stream->config->scale.update; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + } +} + +static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync) +{ + if (stream->id == RKVPSS_OUTPUT_CH0) + poly_phase_scale(stream, on, sync); + else + bilinear_scale(stream, on, sync); + return 0; +} + +static void rkvpss_stream_stop(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + int ret; + + stream->stopping = true; + if (atomic_read(&dev->pipe_stream_cnt) > 0) { + ret = wait_event_timeout(stream->done, !stream->streaming, + msecs_to_jiffies(300)); + if (!ret) + v4l2_warn(&dev->v4l2_dev, "%s id:%d timeout\n", __func__, stream->id); + } + stream->stopping = false; + stream->streaming = false; + if (stream->ops->disable_mi) + stream->ops->disable_mi(stream); + rkvpss_stream_crop(stream, false, true); + rkvpss_stream_scale(stream, false, true); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d\n", __func__, + stream->id); +} + +static void rkvpss_stop_streaming(struct vb2_queue *queue) +{ + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_vdev_node *node = &stream->vnode; + struct rkvpss_device *dev = stream->dev; + struct rkvpss_hw_dev *hw = dev->hw_dev; + + if (!stream->streaming) + return; + + mutex_lock(&hw->dev_lock); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d enter\n", __func__, + node->vdev.name, stream->id); + + if (atomic_read(&dev->pipe_stream_cnt) == 1) { + rkvpss_pipeline_stream(dev, false); + rkvpss_stream_stop(stream); + } else { + rkvpss_stream_stop(stream); + rkvpss_pipeline_stream(dev, false); + } + destroy_buf_queue(stream, VB2_BUF_STATE_ERROR); + rkvpss_pipeline_close(dev); + tasklet_disable(&stream->buf_done_tasklet); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d exit\n", __func__, + node->vdev.name, stream->id); + mutex_unlock(&hw->dev_lock); +} + +static int check_wr_uvswap(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct rkvpss_stream *check_stream; + struct capture_fmt *fmt; + bool wr_uv_swap = false; + int i, ret = 0; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + check_stream = &dev->stream_vdev.stream[i]; + if (check_stream->streaming) { + fmt = &check_stream->out_cap_fmt; + switch (fmt->fourcc) { + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_VYUY: + wr_uv_swap = true; + break; + default: + break; + } + } + } + if (wr_uv_swap) { + switch (stream->out_cap_fmt.fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_UYVY: + v4l2_err(&dev->v4l2_dev, "wr_uv_swap need to be consistent\n"); + ret = -EINVAL; + break; + default: + break; + } + } + + return ret; +} + +static int rkvpss_stream_start(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + int ret = 0; + + if (dev->unite_mode) + calc_unite_scl_params(stream); + + stream->is_crop_upd = true; + ret = rkvpss_stream_scale(stream, true, true); + if (ret < 0) + return ret; + ret = rkvpss_stream_crop(stream, true, true); + if (ret < 0) + return ret; + ret = check_wr_uvswap(stream); + if (ret < 0) + return ret; + + stream->is_pause = true; + + if (stream->ops->config_mi) + stream->ops->config_mi(stream); + + stream->streaming = true; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d enter\n", __func__, + stream->id); + return ret; +} + +static int rkvpss_start_streaming(struct vb2_queue *queue, unsigned int count) +{ + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_vdev_node *node = &stream->vnode; + struct rkvpss_device *dev = stream->dev; + struct rkvpss_hw_dev *hw = dev->hw_dev; + int ret = -EINVAL; + + if (stream->streaming) + return -EBUSY; + + mutex_lock(&hw->dev_lock); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d enter\n", __func__, + node->vdev.name, stream->id); + + if (!dev->inp || !stream->linked) { + v4l2_err(&dev->v4l2_dev, "no link or invalid input source\n"); + goto free_buf_queue; + } + + if (hw->is_ofl_ch[stream->id]) { + v4l2_err(&dev->v4l2_dev, "channel[%d] already assigned to offline", stream->id); + goto free_buf_queue; + } + + rkvpss_pipeline_open(dev); + + ret = rkvpss_stream_start(stream); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "start %s failed\n", node->vdev.name); + goto pipe_close; + } + + ret = rkvpss_pipeline_stream(dev, true); + if (ret < 0) + goto stop_stream; + + tasklet_enable(&stream->buf_done_tasklet); + stream->dbg.id = -1; + stream->dbg.frameloss = 0; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d exit\n", __func__, + node->vdev.name, stream->id); + mutex_unlock(&hw->dev_lock); + return 0; +stop_stream: + stream->streaming = false; + rkvpss_stream_stop(stream); +pipe_close: + rkvpss_pipeline_close(dev); +free_buf_queue: + destroy_buf_queue(stream, VB2_BUF_STATE_QUEUED); + mutex_unlock(&hw->dev_lock); + return ret; +} + +static const struct vb2_ops stream_vb2_ops = { + .queue_setup = rkvpss_queue_setup, + .buf_queue = rkvpss_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .stop_streaming = rkvpss_stop_streaming, + .start_streaming = rkvpss_start_streaming, +}; + +static int rkvpss_init_vb2_queue(struct vb2_queue *q, + struct rkvpss_stream *stream, + enum v4l2_buf_type buf_type) +{ + q->type = buf_type; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; + q->drv_priv = stream; + q->ops = &stream_vb2_ops; + q->mem_ops = stream->dev->hw_dev->mem_ops; + q->buf_struct_size = sizeof(struct rkvpss_buffer); + q->min_buffers_needed = STREAM_OUT_REQ_BUFS_MIN; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + q->lock = &stream->dev->apilock; + q->dev = stream->dev->hw_dev->dev; + q->allow_cache_hints = 1; + q->bidirectional = 1; + if (stream->dev->hw_dev->is_dma_contig) + q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; + q->gfp_flags = GFP_DMA32; + return vb2_queue_init(q); +} + +static const +struct capture_fmt *find_fmt(struct rkvpss_stream *stream, u32 pixelfmt) +{ + const struct capture_fmt *fmt; + u32 i; + + for (i = 0; i < stream->config->fmt_size; i++) { + fmt = &stream->config->fmts[i]; + if (fmt->fourcc == pixelfmt) + return fmt; + } + return NULL; +} + +static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs) +{ + switch (fcc) { + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_YUV444M: + *xsubs = 1; + *ysubs = 1; + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_YVU422M: + case V4L2_PIX_FMT_FBC2: + *xsubs = 2; + *ysubs = 1; + break; + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_FBC0: + *xsubs = 2; + *ysubs = 2; + break; + default: + return -EINVAL; + } + return 0; +} + +static int rkvpss_set_fmt(struct rkvpss_stream *stream, + struct v4l2_pix_format_mplane *pixm, + bool try) +{ + struct rkvpss_device *dev = stream->dev; + u32 i, planes, xsubs = 1, ysubs = 1, imagsize = 0; + const struct capture_fmt *fmt; + + fmt = find_fmt(stream, pixm->pixelformat); + if (!fmt) { + v4l2_err(&dev->v4l2_dev, + "nonsupport pixelformat:%c%c%c%c\n", + pixm->pixelformat, + pixm->pixelformat >> 8, + pixm->pixelformat >> 16, + pixm->pixelformat >> 24); + return -EINVAL; + } + + /* Tile4x4 writing of Channel0 and Channel1 only supports either one.*/ + if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) { + if ((stream->id == 0) && + dev->stream_vdev.stream[1].streaming && + (dev->stream_vdev.stream[1].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || + dev->stream_vdev.stream[1].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)) { + v4l2_err(&dev->v4l2_dev, + "Tile4x4 writing of Ch0 and Cl1 only supports either one\n"); + return -EINVAL; + } + if ((stream->id == 1) && + dev->stream_vdev.stream[0].streaming && + (dev->stream_vdev.stream[0].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || + dev->stream_vdev.stream[0].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)) { + v4l2_err(&dev->v4l2_dev, + "Tile4x4 writing of Ch0 and Cl1 only supports either one\n"); + return -EINVAL; + } + } + + pixm->num_planes = fmt->mplanes; + pixm->field = V4L2_FIELD_NONE; + if (!pixm->quantization) + pixm->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fcc_xysubs(fmt->fourcc, &xsubs, &ysubs); + planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes; + for (i = 0; i < planes; i++) { + struct v4l2_plane_pix_format *plane_fmt; + u32 width, height, bytesperline, w, h; + + plane_fmt = pixm->plane_fmt + i; + w = pixm->width; + h = pixm->height; + width = i ? w / xsubs : w; + height = i ? h / ysubs : h; + + if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) + bytesperline = ALIGN(((width / 4) * fmt->bpp[i]), 16); + else + bytesperline = width * DIV_ROUND_UP(fmt->bpp[i], 8); + + if (i != 0 || plane_fmt->bytesperline < bytesperline) + plane_fmt->bytesperline = bytesperline; + + if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) + plane_fmt->sizeimage = plane_fmt->bytesperline * (height / 4); + else + plane_fmt->sizeimage = plane_fmt->bytesperline * height; + + imagsize += plane_fmt->sizeimage; + } + if (fmt->mplanes == 1) + pixm->plane_fmt[0].sizeimage = imagsize; + if (!try) { + stream->out_cap_fmt = *fmt; + stream->out_fmt = *pixm; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s: stream:%d req(%d, %d) out(%d, %d)\n", + __func__, stream->id, pixm->width, pixm->height, + stream->out_fmt.width, stream->out_fmt.height); + } + return 0; +} + +/************************* v4l2_file_operations***************************/ + +static int rkvpss_fh_open(struct file *file) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct rkvpss_device *dev = stream->dev; + int ret; + + if (!dev->is_probe_end) + return -EINVAL; + + ret = v4l2_fh_open(file); + if (!ret) { + ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "pipeline power on failed %d\n", ret); + vb2_fop_release(file); + } + } + return ret; +} + +static int rkvpss_fh_release(struct file *file) +{ + struct rkvpss_stream *stream = video_drvdata(file); + int ret; + + ret = vb2_fop_release(file); + if (!ret) + v4l2_pipeline_pm_put(&stream->vnode.vdev.entity); + return ret; +} + +static const struct v4l2_file_operations rkvpss_fops = { + .open = rkvpss_fh_open, + .release = rkvpss_fh_release, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = video_ioctl2, +#endif +}; + +static int rkvpss_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, "Camera", sizeof(input->name)); + + return 0; +} + +static int rkvpss_try_fmt_vid_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + + return rkvpss_set_fmt(stream, &f->fmt.pix_mp, true); +} + +static int rkvpss_enum_fmt_vid_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + const struct capture_fmt *fmt = NULL; + + if (f->index >= stream->config->fmt_size) + return -EINVAL; + + fmt = &stream->config->fmts[f->index]; + f->pixelformat = fmt->fourcc; + + switch (f->pixelformat) { + case V4L2_PIX_FMT_TILE420: + strscpy(f->description, + "Rockchip yuv420 tile", + sizeof(f->description)); + break; + case V4L2_PIX_FMT_TILE422: + strscpy(f->description, + "Rockchip yuv422 tile", + sizeof(f->description)); + break; + default: + break; + } + + return 0; +} + +static int rkvpss_s_fmt_vid_mplane(struct file *file, + void *priv, struct v4l2_format *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct video_device *vdev = &stream->vnode.vdev; + struct rkvpss_vdev_node *node = vdev_to_node(vdev); + struct rkvpss_device *dev = stream->dev; + + /* Change not allowed if queue is streaming. */ + if (vb2_is_streaming(&node->buf_queue)) { + v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + return rkvpss_set_fmt(stream, &f->fmt.pix_mp, false); +} + +static int rkvpss_g_fmt_vid_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + + f->fmt.pix_mp = stream->out_fmt; + + return 0; +} + +static int rkvpss_g_selection(struct file *file, void *prv, + struct v4l2_selection *sel) +{ + struct rkvpss_stream *stream = video_drvdata(file); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = stream->crop; + break; + default: + return -EINVAL; + } + return 0; +} + +static int rkvpss_s_selection(struct file *file, void *prv, + struct v4l2_selection *sel) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct rkvpss_device *dev = stream->dev; + struct v4l2_rect *crop = &stream->crop; + u32 max_w = dev->vpss_sdev.in_fmt.width; + u32 max_h = dev->vpss_sdev.in_fmt.height; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + if (sel->flags != 0) + return -EINVAL; + + sel->r.left = ALIGN(sel->r.left, 2); + sel->r.top = ALIGN(sel->r.top, 2); + sel->r.width = ALIGN(sel->r.width, 2); + sel->r.height = ALIGN(sel->r.height, 2); + if (!sel->r.width || !sel->r.height || + sel->r.width + sel->r.left > max_w || + sel->r.height + sel->r.top > max_h) { + v4l2_err(&dev->v4l2_dev, + "invalid crop left:%d top:%d w:%d h:%d for %dx%d\n", + sel->r.left, sel->r.top, sel->r.width, sel->r.height, max_w, max_h); + return -EINVAL; + } + + *crop = sel->r; + stream->is_crop_upd = true; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "stream %d crop(%d,%d)/%dx%d\n", stream->id, + crop->left, crop->top, crop->width, crop->height); + return 0; +} + +static int rkvpss_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct device *dev = stream->dev->dev; + struct video_device *vdev = video_devdata(file); + + strscpy(cap->card, vdev->name, sizeof(cap->card)); + snprintf(cap->driver, sizeof(cap->driver), + "%s_v%d", dev->driver->name, + stream->dev->vpss_ver >> 4); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(dev)); + + return 0; +} + +static void rkvpss_stream_mf(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + u32 val, mask; + + if (!stream->is_mf_upd) + return; + stream->is_mf_upd = false; + + if (!dev->hw_dev->is_ofl_cmsc) { + mask = RKVPSS_MIR_EN; + val = dev->mir_en ? RKVPSS_MIR_EN : 0; + rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CTRL, mask, val); + } + mask = RKVPSS_MI_CHN_V_FLIP(stream->id); + /* Tile4x4 writing can't flip*/ + if (stream->out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || + stream->out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422) + stream->flip_en = false; + val = stream->flip_en ? mask : 0; + rkvpss_unite_set_bits(dev, RKVPSS_MI_WR_VFLIP_CTRL, mask, val); +} + +static int rkvpss_get_mirror_flip(struct rkvpss_stream *stream, + struct rkvpss_mirror_flip *cfg) +{ + cfg->mirror = stream->dev->mir_en; + cfg->flip = stream->flip_en; + return 0; +} + +static int rkvpss_set_mirror_flip(struct rkvpss_stream *stream, + struct rkvpss_mirror_flip *cfg) +{ + struct rkvpss_device *dev = stream->dev; + + if (dev->hw_dev->is_ofl_cmsc && cfg->mirror) { + cfg->mirror = 0; + v4l2_warn(&stream->dev->v4l2_dev, + "mirror mux to vpss offline mode\n"); + } + if (dev->mir_en != !!cfg->mirror || + stream->flip_en != !!cfg->flip) + stream->is_mf_upd = true; + dev->mir_en = !!cfg->mirror; + stream->flip_en = !!cfg->flip; + return 0; +} + +static void cmsc_config_hw(struct rkvpss_device *dev, struct rkvpss_cmsc_cfg *cfg, bool sync, + int unite_index) +{ + int i, j, k, slope, hor; + u32 win_en, mode, val, ctrl = 0; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + win_en = cfg->win[i].win_en; + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "%s unite:%d, unite_index:%d ch:%d win_en:0x%x\n", __func__, + dev->unite_mode, unite_index, i, win_en); + if (win_en) + ctrl |= RKVPSS_CMSC_CHN_EN(i); + rkvpss_idx_write(dev, RKVPSS_CMSC_CHN0_WIN + i * 4, win_en, unite_index); + + mode = cfg->win[i].mode; + rkvpss_idx_write(dev, RKVPSS_CMSC_CHN0_MODE + i * 4, mode, unite_index); + + for (j = 0; j < RKVPSS_CMSC_WIN_MAX && win_en; j++) { + if (!(win_en & BIT(j))) + continue; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + val = RKVPSS_CMSC_WIN_VTX(cfg->win[j].point[k].x, + cfg->win[j].point[k].y); + rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val, + unite_index); + + rkvpss_cmsc_slop(&cfg->win[j].point[k], + (k + 1 == RKVPSS_CMSC_POINT_MAX) ? + &cfg->win[j].point[0] : &cfg->win[j].point[k + 1], + &slope, &hor); + val = RKVPSS_CMSC_WIN_SLP(slope, hor); + rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val, + unite_index); + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "%s unite:%d unite_index:%d ch:%d win:%d point:%d x:%u y:%u", + __func__, dev->unite_mode, unite_index, i, j, k, + cfg->win[j].point[k].x, + cfg->win[j].point[k].y); + } + + if ((mode & BIT(j))) + continue; + val = RKVPSS_CMSK_WIN_YUV(cfg->win[j].cover_color_y, + cfg->win[j].cover_color_u, + cfg->win[j].cover_color_v); + val |= RKVPSS_CMSC_WIN_ALPHA(cfg->win[j].cover_color_a); + rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_PARA + j * 4, val, unite_index); + } + } + + if (ctrl) { + ctrl |= RKVPSS_CMSC_EN; + ctrl |= RKVPSS_CMSC_BLK_SZIE(cfg->mosaic_block); + } + rkvpss_idx_write(dev, RKVPSS_CMSC_CTRL, ctrl, unite_index); + val = RKVPSS_CMSC_GEN_UPD; + if (sync) + val |= RKVPSS_CMSC_FORCE_UPD; + rkvpss_idx_write(dev, RKVPSS_CMSC_UPDATE, val, unite_index); +} + +void rkvpss_cmsc_config_v1(struct rkvpss_device *dev, bool sync) +{ + unsigned long lock_flags = 0; + struct rkvpss_cmsc_cfg left_cfg = {0}, right_cfg = {0}; + struct rkvpss_cmsc_win *win; + struct rkvpss_cmsc_point *point; + int i, j, k; + u32 mask; + + spin_lock_irqsave(&dev->cmsc_lock, lock_flags); + if (dev->hw_dev->is_ofl_cmsc || !dev->cmsc_upd) { + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + return; + } + dev->cmsc_upd = false; + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + + if (dev->unite_mode) { + /* unite left */ + for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { + left_cfg.win[i].cover_color_y = dev->cmsc_cfg.win[i].cover_color_y; + left_cfg.win[i].cover_color_u = dev->cmsc_cfg.win[i].cover_color_u; + left_cfg.win[i].cover_color_v = dev->cmsc_cfg.win[i].cover_color_v; + left_cfg.win[i].cover_color_a = dev->cmsc_cfg.win[i].cover_color_a; + for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) + left_cfg.win[i].point[j] = dev->cmsc_cfg.win[i].point[j]; + } + left_cfg.mosaic_block = dev->cmsc_cfg.mosaic_block; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + left_cfg.win[i].win_en = dev->cmsc_cfg.win[i].win_en; + left_cfg.win[i].mode = dev->cmsc_cfg.win[i].mode; + } + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + win = &left_cfg.win[j]; + if (!(left_cfg.win[i].win_en & BIT(j))) + continue; + mask = 0; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + point = &win->point[k]; + if (point->x >= dev->vpss_sdev.in_fmt.width / 2) + mask |= BIT(k); + else + mask &= ~BIT(k); + } + if (mask == 0xf) { + /** all right **/ + left_cfg.win[i].win_en &= ~BIT(j); + } else if (mask != 0) { + /** middle need avoid pentagon **/ + if (win->point[0].x != win->point[3].x || + win->point[1].x != win->point[2].x) { + left_cfg.win[i].win_en &= ~BIT(j); + } else { + win->point[1].x = dev->vpss_sdev.in_fmt.width / 2; + win->point[2].x = dev->vpss_sdev.in_fmt.width / 2; + } + } + } + } + + /* unite right */ + for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { + right_cfg.win[i].cover_color_y = dev->cmsc_cfg.win[i].cover_color_y; + right_cfg.win[i].cover_color_u = dev->cmsc_cfg.win[i].cover_color_u; + right_cfg.win[i].cover_color_v = dev->cmsc_cfg.win[i].cover_color_v; + right_cfg.win[i].cover_color_a = dev->cmsc_cfg.win[i].cover_color_a; + + for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) + right_cfg.win[i].point[j] = dev->cmsc_cfg.win[i].point[j]; + } + right_cfg.mosaic_block = dev->cmsc_cfg.mosaic_block; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + right_cfg.win[i].win_en = dev->cmsc_cfg.win[i].win_en; + right_cfg.win[i].mode = dev->cmsc_cfg.win[i].mode; + } + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + win = &right_cfg.win[j]; + if (!(right_cfg.win[i].win_en & BIT(j))) + continue; + mask = 0; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + point = &win->point[k]; + if (point->x <= dev->vpss_sdev.in_fmt.width / 2) + mask |= BIT(k); + else + mask &= ~BIT(k); + } + if (mask == 0xf) { + /** all left **/ + right_cfg.win[i].win_en &= ~BIT(j); + } else if (mask != 0) { + /** middle need avoid pentagon **/ + if (win->point[0].x != win->point[3].x || + win->point[1].x != win->point[2].x) { + right_cfg.win[i].win_en &= ~BIT(j); + } else { + win->point[0].x = RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[3].x = RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[1].x = win->point[1].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[2].x = win->point[2].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + } + } else { + /** all right **/ + win->point[0].x = win->point[0].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[1].x = win->point[1].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[2].x = win->point[2].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[3].x = win->point[3].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + } + } + } + + cmsc_config_hw(dev, &left_cfg, sync, VPSS_UNITE_LEFT); + cmsc_config_hw(dev, &right_cfg, sync, VPSS_UNITE_RIGHT); + } else { + cmsc_config_hw(dev, &dev->cmsc_cfg, sync, VPSS_UNITE_LEFT); + } +} + +static int rkvpss_get_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg) +{ + struct rkvpss_device *dev = stream->dev; + unsigned long lock_flags = 0; + u32 i, win_en, mode; + + spin_lock_irqsave(&dev->cmsc_lock, lock_flags); + *cfg = dev->cmsc_cfg; + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + + win_en = cfg->win[stream->id].win_en; + mode = cfg->win[stream->id].mode; + for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { + cfg->win[i].win_en = !!(win_en & BIT(i)); + cfg->win[i].mode = !!(mode & BIT(i)); + } + cfg->width_ro = dev->vpss_sdev.in_fmt.width; + cfg->height_ro = dev->vpss_sdev.in_fmt.height; + return 0; +} + +static int rkvpss_set_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg) +{ + struct rkvpss_device *dev = stream->dev; + unsigned long lock_flags = 0; + u16 i, j, k, win_en = 0, mode = 0; + int ret = 0; + + if (dev->hw_dev->is_ofl_cmsc) + return -EINVAL; + + spin_lock_irqsave(&dev->cmsc_lock, lock_flags); + for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { + if (!cfg->win[i].win_en) + continue; + + win_en |= BIT(i); + mode |= cfg->win[i].mode ? BIT(i) : 0; + + for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) { + for (k = j + 1; k < RKVPSS_CMSC_POINT_MAX; k++) { + if (cfg->win[i].point[j].x == cfg->win[i].point[k].x && + cfg->win[i].point[j].y == cfg->win[i].point[k].y) { + v4l2_warn(&dev->v4l2_dev, + "points should different, P%d(%d,%d) P%d(%d,%d)\n", + j, + cfg->win[i].point[j].x, + cfg->win[i].point[j].y, + k, + cfg->win[i].point[k].x, + cfg->win[i].point[k].y); + ret = -EINVAL; + goto unlock; + } + } + } + if (!cfg->win[i].mode) { + dev->cmsc_cfg.win[i].cover_color_y = cfg->win[i].cover_color_y; + dev->cmsc_cfg.win[i].cover_color_u = cfg->win[i].cover_color_u; + dev->cmsc_cfg.win[i].cover_color_v = cfg->win[i].cover_color_v; + dev->cmsc_cfg.win[i].cover_color_a = cfg->win[i].cover_color_a; + if (dev->cmsc_cfg.win[i].cover_color_a > 15) + dev->cmsc_cfg.win[i].cover_color_a = 15; + } + for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) + dev->cmsc_cfg.win[i].point[j] = cfg->win[i].point[j]; + } + dev->cmsc_cfg.win[stream->id].win_en = win_en; + dev->cmsc_cfg.win[stream->id].mode = mode; + dev->cmsc_cfg.mosaic_block = cfg->mosaic_block; + dev->cmsc_upd = true; + v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, + "%s ch:%d win_en:0x%x", + __func__, stream->id, + dev->cmsc_cfg.win[stream->id].win_en); + +unlock: + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + return ret; +} + +static long rkvpss_ioctl_default(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct rkvpss_stream *stream = video_drvdata(file); + long ret = 0; + + if (!arg) + return -EINVAL; + + switch (cmd) { + case RKVPSS_CMD_GET_MIRROR_FLIP: + ret = rkvpss_get_mirror_flip(stream, arg); + break; + case RKVPSS_CMD_SET_MIRROR_FLIP: + ret = rkvpss_set_mirror_flip(stream, arg); + break; + case RKVPSS_CMD_GET_CMSC: + ret = rkvpss_get_cmsc(stream, arg); + break; + case RKVPSS_CMD_SET_CMSC: + ret = rkvpss_set_cmsc(stream, arg); + break; + case RKVPSS_CMD_STREAM_ATTACH_INFO: + stream->is_attach_info = *(int *)arg; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ioctl_ops rkvpss_v4l2_ioctl_ops = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = rkvpss_enum_input, + .vidioc_try_fmt_vid_cap_mplane = rkvpss_try_fmt_vid_mplane, + .vidioc_enum_fmt_vid_cap = rkvpss_enum_fmt_vid_mplane, + .vidioc_s_fmt_vid_cap_mplane = rkvpss_s_fmt_vid_mplane, + .vidioc_g_fmt_vid_cap_mplane = rkvpss_g_fmt_vid_mplane, + .vidioc_s_selection = rkvpss_s_selection, + .vidioc_g_selection = rkvpss_g_selection, + .vidioc_querycap = rkvpss_querycap, + .vidioc_default = rkvpss_ioctl_default, +}; + +static void rkvpss_unregister_stream_video(struct rkvpss_stream *stream) +{ + tasklet_kill(&stream->buf_done_tasklet); + media_entity_cleanup(&stream->vnode.vdev.entity); + video_unregister_device(&stream->vnode.vdev); +} + +static int rkvpss_register_stream_video(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct video_device *vdev = &stream->vnode.vdev; + struct rkvpss_vdev_node *node; + enum v4l2_buf_type buf_type; + int ret = 0; + + node = vdev_to_node(vdev); + vdev->release = video_device_release_empty; + vdev->fops = &rkvpss_fops; + vdev->minor = -1; + vdev->v4l2_dev = v4l2_dev; + vdev->lock = &dev->apilock; + video_set_drvdata(vdev, stream); + + vdev->ioctl_ops = &rkvpss_v4l2_ioctl_ops; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; + vdev->vfl_dir = VFL_DIR_RX; + node->pad.flags = MEDIA_PAD_FL_SINK; + buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + rkvpss_init_vb2_queue(&node->buf_queue, stream, buf_type); + vdev->queue = &node->buf_queue; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + v4l2_err(v4l2_dev, + "video register failed with error %d\n", ret); + return ret; + } + + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); + if (ret < 0) + goto unreg; + + INIT_LIST_HEAD(&stream->buf_done_list); + tasklet_init(&stream->buf_done_tasklet, + rkvpss_buf_done_task, (unsigned long)stream); + tasklet_disable(&stream->buf_done_tasklet); + return 0; +unreg: + video_unregister_device(vdev); + return ret; +} + +void rkvpss_stream_default_fmt_v1(struct rkvpss_device *dev, u32 id, + u32 width, u32 height, u32 pixelformat) +{ + struct rkvpss_stream *stream = &dev->stream_vdev.stream[id]; + struct v4l2_pix_format_mplane pixm = { 0 }; + + if (pixelformat) + pixm.pixelformat = pixelformat; + else + pixm.pixelformat = stream->out_cap_fmt.fourcc; + if (!pixm.pixelformat) + return; + + stream->crop.left = 0; + stream->crop.top = 0; + stream->crop.width = width; + stream->crop.height = height; + + pixm.width = width; + pixm.height = height; + rkvpss_set_fmt(stream, &pixm, false); +} + +int rkvpss_register_stream_vdevs_v1(struct rkvpss_device *dev) +{ + struct rkvpss_stream_vdev *stream_vdev; + struct rkvpss_stream *stream; + struct video_device *vdev; + char *vdev_name; + int i, j, ret = 0; + + stream_vdev = &dev->stream_vdev; + memset(stream_vdev, 0, sizeof(*stream_vdev)); + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + stream = &stream_vdev->stream[i]; + stream->id = i; + stream->dev = dev; + vdev = &stream->vnode.vdev; + INIT_LIST_HEAD(&stream->buf_queue); + init_waitqueue_head(&stream->done); + spin_lock_init(&stream->vbq_lock); + switch (i) { + case RKVPSS_OUTPUT_CH0: + vdev_name = S0_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl0_config; + break; + case RKVPSS_OUTPUT_CH1: + vdev_name = S1_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl1_config; + break; + case RKVPSS_OUTPUT_CH2: + vdev_name = S2_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl2_config; + break; + case RKVPSS_OUTPUT_CH3: + vdev_name = S3_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl3_config; + break; + default: + v4l2_err(&dev->v4l2_dev, "Invalid stream:%d\n", i); + return -EINVAL; + } + strscpy(vdev->name, vdev_name, sizeof(vdev->name)); + ret = rkvpss_register_stream_video(stream); + if (ret < 0) + goto err; + } + return 0; +err: + for (j = 0; j < i; j++) { + stream = &stream_vdev->stream[j]; + rkvpss_unregister_stream_video(stream); + } + return ret; +} + +void rkvpss_unregister_stream_vdevs_v1(struct rkvpss_device *dev) +{ + struct rkvpss_stream_vdev *stream_vdev; + struct rkvpss_stream *stream; + int i; + + stream_vdev = &dev->stream_vdev; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + stream = &stream_vdev->stream[i]; + rkvpss_unregister_stream_video(stream); + } +} + +void rkvpss_isr_v1(struct rkvpss_device *dev, u32 mis_val) +{ + v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, + "isr:0x%x\n", mis_val); + dev->isr_cnt++; + if (mis_val & RKVPSS_ISP_ALL_FRM_END && dev->remote_sd) + rkvpss_check_idle(dev, VPSS_FRAME_END); +} + +void rkvpss_mi_isr_v1(struct rkvpss_device *dev, u32 mis_val) +{ + struct rkvpss_stream *stream; + int i, ris = readl(dev->hw_dev->base_addr + RKVPSS_MI_RIS); + + v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, + "mi isr:0x%x, ris:0x%x\n", mis_val, ris); + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + stream = &dev->stream_vdev.stream[i]; + + if (!stream->streaming || + !(ris & stream->config->frame_end_id)) + continue; + writel(stream->config->frame_end_id, dev->hw_dev->base_addr + RKVPSS_MI_ICR); + + if (!dev->unite_mode || dev->unite_index == VPSS_UNITE_RIGHT) { + if (stream->stopping) { + stream->stopping = false; + stream->streaming = false; + stream->ops->disable_mi(stream); + wake_up(&stream->done); + } else { + rkvpss_frame_end(stream); + } + } + } + rkvpss_check_idle(dev, (ris & 0xf) << 3); +} diff --git a/drivers/media/platform/rockchip/vpss/stream_v1.h b/drivers/media/platform/rockchip/vpss/stream_v1.h new file mode 100644 index 000000000000..df0071022384 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/stream_v1.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_STREAM_V1_H +#define _RKVPSS_STREAM_V1_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_VPSS_V1) +int rkvpss_register_stream_vdevs_v1(struct rkvpss_device *dev); +void rkvpss_unregister_stream_vdevs_v1(struct rkvpss_device *dev); +void rkvpss_stream_default_fmt_v1(struct rkvpss_device *dev, u32 id, + u32 width, u32 height, u32 pixelformat); +void rkvpss_isr_v1(struct rkvpss_device *dev, u32 mis_val); +void rkvpss_mi_isr_v1(struct rkvpss_device *dev, u32 mis_val); +void rkvpss_cmsc_config_v1(struct rkvpss_device *dev, bool sync); +int rkvpss_stream_buf_cnt_v1(struct rkvpss_stream *stream); + +#else +static inline int rkvpss_register_stream_vdevs_v1(struct rkvpss_device *dev) {return -EINVAL; } +static inline void rkvpss_unregister_stream_vdevs_v1(struct rkvpss_device *dev) {} +static inline void rkvpss_stream_default_fmt_v1(struct rkvpss_device *dev, u32 id, u32 width, u32 height, u32 pixelformat) {} +static inline void rkvpss_isr_v1(struct rkvpss_device *dev, u32 mis_val) {} +static inline void rkvpss_mi_isr_v1(struct rkvpss_device *dev, u32 mis_val) {} +static inline void rkvpss_cmsc_config_v1(struct rkvpss_device *dev, bool sync) {} +static inline int rkvpss_stream_buf_cnt_v1(struct rkvpss_stream *stream) {return -EINVAL; } + +#endif + +#endif diff --git a/drivers/media/platform/rockchip/vpss/version.h b/drivers/media/platform/rockchip/vpss/version.h index 50db86e796a3..0afe1a22271b 100644 --- a/drivers/media/platform/rockchip/vpss/version.h +++ b/drivers/media/platform/rockchip/vpss/version.h @@ -4,7 +4,6 @@ #ifndef _RKVPSS_VERSION_H #define _RKVPSS_VERSION_H #include -#include /* * RKVPSS DRIVER VERSION NOTE diff --git a/drivers/media/platform/rockchip/vpss/vpss.c b/drivers/media/platform/rockchip/vpss/vpss.c index 5da9d9937cf5..f2637ec6a446 100644 --- a/drivers/media/platform/rockchip/vpss/vpss.c +++ b/drivers/media/platform/rockchip/vpss/vpss.c @@ -1,16 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include -#include -#include - +#include "vpss.h" +#include "common.h" +#include "stream.h" #include "dev.h" -#include "regs.h" +#include "vpss_offline.h" +#include "hw.h" +#include "procfs.h" +#include "regs_v1.h" static const struct vpsssd_fmt rkvpss_formats[] = { { @@ -19,6 +17,22 @@ static const struct vpsssd_fmt rkvpss_formats[] = { }, }; +static inline const char *s_dev_name(int i) +{ + switch (i) { + case 0: + return S0_VDEV_NAME; + case 1: + return S1_VDEV_NAME; + case 2: + return S2_VDEV_NAME; + case 3: + return S3_VDEV_NAME; + default: + return S0_VDEV_NAME; + } +} + static int rkvpss_subdev_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, @@ -29,6 +43,7 @@ static int rkvpss_subdev_link_setup(struct media_entity *entity, struct rkvpss_device *dev; struct rkvpss_stream_vdev *vdev; struct rkvpss_stream *stream = NULL; + int i; if (local->index != RKVPSS_PAD_SINK && local->index != RKVPSS_PAD_SOURCE) @@ -46,22 +61,23 @@ static int rkvpss_subdev_link_setup(struct media_entity *entity, if (vpss_sdev->state & VPSS_START) return -EBUSY; - if (!strcmp(remote->entity->name, S0_VDEV_NAME)) { - stream = &vdev->stream[RKVPSS_OUTPUT_CH0]; - } else if (!strcmp(remote->entity->name, S1_VDEV_NAME)) { - stream = &vdev->stream[RKVPSS_OUTPUT_CH1]; - } else if (!strcmp(remote->entity->name, S2_VDEV_NAME)) { - stream = &vdev->stream[RKVPSS_OUTPUT_CH2]; - } else if (!strcmp(remote->entity->name, S3_VDEV_NAME)) { - stream = &vdev->stream[RKVPSS_OUTPUT_CH3]; - } else if (strstr(remote->entity->name, "rkisp")) { + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) { + if (!strcmp(remote->entity->name, s_dev_name(i))) { + stream = &vdev->stream[i]; + break; + } + } + + if (stream) + stream->linked = flags & MEDIA_LNK_FL_ENABLED; + + if (strstr(remote->entity->name, "rkisp")) { if (flags & MEDIA_LNK_FL_ENABLED) dev->inp = INP_ISP; else dev->inp = INP_INVAL; } - if (stream) - stream->linked = flags & MEDIA_LNK_FL_ENABLED; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, "input:%d\n", dev->inp); return 0; } @@ -253,7 +269,7 @@ static int rkvpss_sof(struct rkvpss_subdev *sdev, struct rkisp_vpss_sof *info) dev->unite_mode, dev->unite_index, info->seq); rkvpss_cmsc_config(dev, !info->irq); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) { stream = &dev->stream_vdev.stream[i]; if (!stream->streaming) continue; @@ -287,7 +303,7 @@ static int rkvpss_sof(struct rkvpss_subdev *sdev, struct rkisp_vpss_sof *info) /* force update mi write */ vpss_online = rkvpss_hw_read(hw, RKVPSS_VPSS_ONLINE); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) { if (((vpss_online >> (2 * i)) & 0x3) == 0x2) val |= BIT(i); } @@ -296,7 +312,7 @@ static int rkvpss_sof(struct rkvpss_subdev *sdev, struct rkisp_vpss_sof *info) } dev->irq_ends_mask = VPSS_FRAME_END; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) { if (hw->is_ofl_ch[i]) continue; if (rkvpss_hw_read(dev->hw_dev, RKVPSS_MI_CHN0_WR_CTRL_SHD + i * 0x100) & 0x1) @@ -426,16 +442,29 @@ static void rkvpss_end_notify_isp(struct rkvpss_device *dev) void rkvpss_check_idle(struct rkvpss_device *dev, u32 irq) { + unsigned long lock_flags = 0; + + spin_lock_irqsave(&dev->idle_lock, lock_flags); dev->irq_ends |= (irq & dev->irq_ends_mask); v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, "%s irq:0x%x ends:0x%x mask:0x%x\n", __func__, irq, dev->irq_ends, dev->irq_ends_mask); - if ((dev->irq_ends & dev->irq_ends_mask) != dev->irq_ends_mask) + if ((dev->irq_ends & dev->irq_ends_mask) != dev->irq_ends_mask) { + spin_unlock_irqrestore(&dev->idle_lock, lock_flags); return; + } + + /* offline MI frame end */ + if (!dev->irq_ends_mask) { + spin_unlock_irqrestore(&dev->idle_lock, lock_flags); + return; + } dev->irq_ends = 0; + spin_unlock_irqrestore(&dev->idle_lock, lock_flags); + rkvpss_end_notify_isp(dev); dev->is_idle = true; diff --git a/drivers/media/platform/rockchip/vpss/vpss.h b/drivers/media/platform/rockchip/vpss/vpss.h index 7b17ccf66288..da8037c72e63 100644 --- a/drivers/media/platform/rockchip/vpss/vpss.h +++ b/drivers/media/platform/rockchip/vpss/vpss.h @@ -4,7 +4,31 @@ #ifndef _RKVPSS_VPSS_H #define _RKVPSS_VPSS_H -#include "common.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + #define GRP_ID_VPSS BIT(0) diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.c b/drivers/media/platform/rockchip/vpss/vpss_offline.c index a26cf8554c6b..f7fcfa659975 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.c +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.c @@ -1,2231 +1,92 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include "vpss.h" +#include "common.h" +#include "stream.h" #include "dev.h" +#include "vpss_offline.h" #include "hw.h" -#include "regs.h" +#include "regs_v1.h" -struct rkvpss_output_ch { - u32 ctrl; - u32 size; - u32 c_offs; -}; +#include "vpss_offline_v1.h" -struct rkvpss_offline_buf { - struct list_head list; - struct vb2_buffer vb; - struct vb2_queue vb2_queue; - struct file *file; - struct dma_buf *dbuf; - void *mem; - int dev_id; - int fd; - bool alloc; -}; - -static void init_vb2(struct rkvpss_offline_dev *ofl, - struct rkvpss_offline_buf *buf) +void rkvpss_dump_reg(struct rkvpss_offline_dev *ofl, int sequence, int size) { struct rkvpss_hw_dev *hw = ofl->hw; - unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING; + struct file *filep = NULL; + char buf[256] = {0}; + loff_t pos = 0; + int i, j; - if (!buf) + if (!IS_ENABLED(CONFIG_NO_GKI)) return; - memset(&buf->vb, 0, sizeof(buf->vb)); - memset(&buf->vb2_queue, 0, sizeof(buf->vb2_queue)); - buf->vb2_queue.gfp_flags = GFP_KERNEL | GFP_DMA32; - buf->vb2_queue.dma_dir = DMA_BIDIRECTIONAL; - if (hw->is_dma_contig) - attrs |= DMA_ATTR_FORCE_CONTIGUOUS; - buf->vb2_queue.dma_attrs = attrs; - buf->vb.vb2_queue = &buf->vb2_queue; -} -static void buf_del(struct file *file, int id, int fd, bool is_all, bool running) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - const struct vb2_mem_ops *ops = hw->mem_ops; - struct rkvpss_offline_buf *buf, *next; - - mutex_lock(&hw->dev_lock); - list_for_each_entry_safe(buf, next, &ofl->list, list) { - if (buf->file == file && (is_all || buf->fd == fd)) { - if (!is_all && running && buf->alloc) - break; - v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, - "%s file:%p dev_id:%d fd:%d dbuf:%p\n", - __func__, file, buf->dev_id, buf->fd, buf->dbuf); - if (!buf->alloc) { - ops->unmap_dmabuf(buf->mem); - ops->detach_dmabuf(buf->mem); - dma_buf_put(buf->dbuf); - } else { - dma_buf_put(buf->dbuf); - ops->put(buf->mem); - } - buf->file = NULL; - buf->mem = NULL; - buf->dbuf = NULL; - buf->fd = -1; - list_del(&buf->list); - kfree(buf); - if (!is_all) - break; - } - } - mutex_unlock(&hw->dev_lock); -} - -static struct rkvpss_offline_buf *buf_add(struct file *file, int id, int fd, int size) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - const struct vb2_mem_ops *ops = hw->mem_ops; - struct rkvpss_offline_buf *buf = NULL, *next = NULL; - struct dma_buf *dbuf; - void *mem = NULL; - bool is_add = true; - - dbuf = dma_buf_get(fd); - if (IS_ERR_OR_NULL(dbuf)) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d invalid dmabuf fd:%d", id, fd); - return buf; - } - if (size && dbuf->size < size) { - v4l2_err(&ofl->v4l2_dev, - "dev_id:%d input fd:%d size error:%zu < %u\n", - id, fd, dbuf->size, size); - dma_buf_put(dbuf); - return buf; - } - - mutex_lock(&hw->dev_lock); - list_for_each_entry_safe(buf, next, &ofl->list, list) { - if (buf->file == file && buf->fd == fd && buf->dbuf == dbuf) { - is_add = false; - break; - } - } - - if (is_add) { - buf = kzalloc(sizeof(struct rkvpss_offline_buf), GFP_KERNEL); - if (!buf) - goto end; - init_vb2(ofl, buf); - mem = ops->attach_dmabuf(&buf->vb, hw->dev, dbuf, dbuf->size); - if (IS_ERR(mem)) { - v4l2_err(&ofl->v4l2_dev, "failed to attach dmabuf, fd:%d\n", fd); - dma_buf_put(dbuf); - kfree(buf); - buf = NULL; - goto end; - } - if (ops->map_dmabuf(mem)) { - v4l2_err(&ofl->v4l2_dev, "failed to map, fd:%d\n", fd); - ops->detach_dmabuf(mem); - dma_buf_put(dbuf); - mem = NULL; - kfree(buf); - buf = NULL; - goto end; - } - buf->dev_id = id; - buf->fd = fd; - buf->file = file; - buf->dbuf = dbuf; - buf->mem = mem; - buf->alloc = false; - list_add_tail(&buf->list, &ofl->list); - v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, - "%s file:%p dev_id:%d fd:%d dbuf:%p size:%d\n", __func__, - file, id, fd, dbuf, size); - } else { - dma_buf_put(dbuf); - } -end: - mutex_unlock(&hw->dev_lock); - return buf; -} - -static int internal_buf_alloc(struct file *file, struct rkvpss_buf_info *info) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - const struct vb2_mem_ops *ops = hw->mem_ops; - struct rkvpss_offline_buf *buf; - struct dma_buf *dbuf; - int fd, i, size; - void *mem; - - for (i = 0; i < info->buf_cnt; i++) { - info->buf_fd[i] = -1; - size = PAGE_ALIGN(info->buf_size[i]); - if (!size) - continue; - buf = kzalloc(sizeof(struct rkvpss_offline_buf), GFP_KERNEL); - if (!buf) - goto err; - init_vb2(ofl, buf); - mem = ops->alloc(&buf->vb, hw->dev, size); - if (IS_ERR_OR_NULL(mem)) { - kfree(buf); - goto err; - } - dbuf = ops->get_dmabuf(&buf->vb, mem, O_RDWR); - if (IS_ERR_OR_NULL(dbuf)) { - ops->put(mem); - kfree(buf); - goto err; - } - fd = dma_buf_fd(dbuf, O_CLOEXEC); - if (fd < 0) { - dma_buf_put(dbuf); - ops->put(mem); - kfree(buf); - goto err; - } - get_dma_buf(dbuf); - - info->buf_fd[i] = fd; - buf->fd = fd; - buf->file = file; - buf->dbuf = dbuf; - buf->mem = mem; - buf->alloc = true; - buf->dev_id = info->dev_id; - ops->prepare(buf->mem); - mutex_lock(&hw->dev_lock); - list_add_tail(&buf->list, &ofl->list); - mutex_unlock(&hw->dev_lock); - v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, - "%s file:%p dev_id:%d fd:%d dbuf:%p\n", - __func__, file, buf->dev_id, fd, dbuf); - } - return 0; -err: - for (i -= 1; i >= 0; i--) - buf_del(file, info->dev_id, info->buf_fd[i], false, false); - return -ENOMEM; -} - -static int external_buf_add(struct file *file, struct rkvpss_buf_info *info) -{ - void *mem; - int i; - - for (i = 0; i < info->buf_cnt; i++) { - mem = buf_add(file, info->dev_id, info->buf_fd[i], info->buf_size[i]); - if (!mem) - goto err; - } - return 0; -err: - for (i -= 1; i >= 0; i--) - buf_del(file, info->dev_id, info->buf_fd[i], false, false); - return -ENOMEM; -} - -static int rkvpss_ofl_buf_add(struct file *file, struct rkvpss_buf_info *info) -{ - int ret; - - if (info->buf_alloc) - ret = internal_buf_alloc(file, info); - else - ret = external_buf_add(file, info); - return ret; -} - -static void rkvpss_ofl_buf_del(struct file *file, struct rkvpss_buf_info *info) -{ - int i; - - for (i = 0; i < info->buf_cnt; i++) - buf_del(file, info->dev_id, info->buf_fd[i], false, false); -} - -static void poly_phase_scale(struct rkvpss_frame_cfg *frame_cfg, - struct rkvpss_offline_dev *ofl, - struct rkvpss_output_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_hw_dev *hw = ofl->hw; - u32 in_w = cfg->crop_width, in_h = cfg->crop_height; - u32 out_w = cfg->scl_width, out_h = cfg->scl_height; - u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac; - u32 i, j, idx, ratio, val, in_div, out_div, factor; - bool dering_en = false, yuv420_in = false, yuv422_to_420 = false; - - if (in_w == out_w && in_h == out_w) { - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, 0); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, 0); - rkvpss_hw_clear_bits(hw, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS); - goto end; - } else { - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS, - RKVPSS_SCL0_CKG_DIS); - } - - /* TODO diff for input and output format */ - if (yuv420_in) { - in_div = 2; - out_div = 2; - } else if (yuv422_to_420) { - in_div = 1; - out_div = 2; - } else { - in_div = 1; - out_div = 1; - } - - if (unite) { - if (left) { - if (in_w == out_w) - val = (cfg->crop_width / 2 - 1) | - ((cfg->crop_height - 1) << 16); - else - val = (cfg->crop_width / 2 + UNITE_ENLARGE - 1) | - ((cfg->crop_height - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); - } else { - val = (ALIGN(ofl->unite_params[0].right_scl_need_size_y + 3, 2) - 1) | - ((cfg->crop_height - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); - val = (ALIGN(ofl->unite_params[0].right_scl_need_size_c + 6, 2) - 1) | - ((cfg->crop_height - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); - } - val = (cfg->scl_width / 2 - 1) | ((cfg->scl_height - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); - } else { - val = (in_w - 1) | ((in_h - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); - val = (out_w - 1) | ((out_h - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); - } - - ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; - if (dering_en) { - ctrl |= RKVPSS_ZME_DERING_EN; - rkvpss_hw_write(hw, RKVPSS_ZME_Y_DERING_PARA, 0xd10410); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_DERING_PARA, 0xd10410); - } - if (in_w != out_w) { - if (in_w > out_w) { - factor = 4096; - ctrl |= RKVPSS_ZME_XSD_EN; - } else { - factor = 65536; - ctrl |= RKVPSS_ZME_XSU_EN; - } - y_xscl_fac = (in_w - 1) * factor / (out_w - 1); - uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1); - - ratio = y_xscl_fac * 10000 / factor; - idx = rkvpss_get_zme_tap_coe_index(ratio); - for (i = 0; i < 17; i++) { - for (j = 0; j < 8; j += 2) { - val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j], - rkvpss_zme_tap8_coe[idx][i][j + 1]); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, val); - } - } - } else { - y_xscl_fac = 0; - uv_xscl_fac = 0; - } - if (unite && !left) { - rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac | - (ofl->unite_params[0].y_w_phase << 16)); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac | - (ofl->unite_params[0].c_w_phase << 16)); - } else { - rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); - } - - if (in_h != out_h) { - if (in_h > out_h) { - factor = 4096; - ctrl |= RKVPSS_ZME_YSD_EN; - } else { - factor = 65536; - ctrl |= RKVPSS_ZME_YSU_EN; - } - y_yscl_fac = (in_h - 1) * factor / (out_h - 1); - uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1); - - ratio = y_yscl_fac * 10000 / factor; - idx = rkvpss_get_zme_tap_coe_index(ratio); - for (i = 0; i < 17; i++) { - for (j = 0; j < 8; j += 2) { - val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j], - rkvpss_zme_tap6_coe[idx][i][j + 1]); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, val); - } - } - } else { - y_yscl_fac = 0; - uv_yscl_fac = 0; - } - rkvpss_hw_write(hw, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac); - - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, ctrl); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, ctrl); - - if (unite) { - val = cfg->scl_width / 2; - rkvpss_hw_write(hw, RKVPSS_ZME_H_SIZE, (val << 16) | val); - rkvpss_hw_write(hw, RKVPSS_ZME_H_OFFS, 0); - if (!left) { - val = cfg->scl_width / 2 - ALIGN_DOWN(cfg->scl_width / 2, 16); - rkvpss_hw_write(hw, RKVPSS_ZME_H_OFFS, (3 << 20) | (3 << 16) | - (ofl->unite_params[0].scl_in_crop_w_y << 8) | - (ofl->unite_params[0].scl_in_crop_w_c << 12) | - (val << 4) | val); - } - } - - ctrl = RKVPSS_ZME_GATING_EN; - if (yuv420_in) - ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; - if (yuv422_to_420) - ctrl |= RKVPSS_ZME_422TO420_EN; - if (unite) { - if (left) - ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_8K_EN; - else - ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_IN_CLIP_EN | RKVPSS_ZME_8K_EN; - } - rkvpss_hw_write(hw, RKVPSS_ZME_CTRL, ctrl); -end: - val = RKVPSS_ZME_GEN_UPD | RKVPSS_ZME_FORCE_UPD; - rkvpss_hw_write(hw, RKVPSS_ZME_UPDATE, val); - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s unite:%d left:%d hw ctrl:0x%x y_src_size:0x%x y_dst_size:0x%x\n", - __func__, unite, left, - rkvpss_hw_read(hw, RKVPSS_ZME_CTRL), - rkvpss_hw_read(hw, RKVPSS_ZME_Y_SRC_SIZE), - rkvpss_hw_read(hw, RKVPSS_ZME_Y_DST_SIZE)); -} - -static void bilinear_scale(struct rkvpss_frame_cfg *frame_cfg, - struct rkvpss_offline_dev *ofl, - struct rkvpss_output_cfg *cfg, int idx, bool unite, bool left) -{ - struct rkvpss_hw_dev *hw = ofl->hw; - u32 in_w = cfg->crop_width, in_h = cfg->crop_height; - u32 out_w = cfg->scl_width, out_h = cfg->scl_height; - u32 reg_base, in_div, out_div, val, ctrl = 0, clk_mask = 0; - bool yuv420_in = false, yuv422_to_420 = false; - - switch (idx) { - case RKVPSS_OUTPUT_CH1: - reg_base = RKVPSS_SCALE1_BASE; - clk_mask = RKVPSS_SCL1_CKG_DIS; - break; - case RKVPSS_OUTPUT_CH2: - reg_base = RKVPSS_SCALE2_BASE; - clk_mask = RKVPSS_SCL2_CKG_DIS; - break; - case RKVPSS_OUTPUT_CH3: - reg_base = RKVPSS_SCALE3_BASE; - clk_mask = RKVPSS_SCL3_CKG_DIS; - break; - default: + filep = filp_open(rkvpss_regfile, O_RDWR | O_APPEND | O_CREAT, 0644); + if (IS_ERR(filep)) { + v4l2_err(&ofl->v4l2_dev, "Open file %s error\n", + rkvpss_regfile); return; - } - - /*config scl clk gate*/ - if (in_w == out_w && in_h == out_h) - rkvpss_hw_clear_bits(hw, RKVPSS_VPSS_CLK_GATE, clk_mask); - else - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CLK_GATE, clk_mask, clk_mask); - - if (!unite) { - if (in_w == out_w && in_h == out_w) - goto end; - - /* TODO diff for input and output format */ - if (yuv420_in) { - in_div = 2; - out_div = 2; - } else if (yuv422_to_420) { - in_div = 1; - out_div = 2; - } else { - in_div = 1; - out_div = 1; - } - - val = in_w | (in_h << 16); - rkvpss_hw_write(hw, reg_base + 0x8, val); - val = out_w | (out_h << 16); - rkvpss_hw_write(hw, reg_base + 0xc, val); - - if (in_w != out_w) { - val = (in_w - 1) * 4096 / (out_w - 1); - rkvpss_hw_write(hw, reg_base + 0x10, val); - val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); - rkvpss_hw_write(hw, reg_base + 0x14, val); - - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - if (in_h != out_h) { - val = (in_h - 1) * 4096 / (out_h - 1); - rkvpss_hw_write(hw, reg_base + 0x18, val); - val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); - rkvpss_hw_write(hw, reg_base + 0x1c, val); - - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; - } } else { - if (left) { - rkvpss_hw_write(hw, reg_base + 0x50, 0); - rkvpss_hw_write(hw, reg_base + 0x20, 0); - rkvpss_hw_write(hw, reg_base + 0x24, 0); - rkvpss_hw_write(hw, reg_base + 0x48, 0); - rkvpss_hw_write(hw, reg_base + 0x4c, 0); - if (in_w == out_w) - val = (cfg->crop_width / 2) | (cfg->crop_height << 16); - else - val = (cfg->crop_width / 2 + UNITE_ENLARGE) | - (cfg->crop_height << 16); - rkvpss_hw_write(hw, reg_base + 0x8, val); - val = cfg->scl_width / 2 | (cfg->scl_height << 16); - rkvpss_hw_write(hw, reg_base + 0xc, val); - ctrl |= RKVPSS_SCL_CLIP_EN; - } else { - val = ofl->unite_params[idx].scl_in_crop_w_y | - (ofl->unite_params[idx].scl_in_crop_w_c << 4); - rkvpss_hw_write(hw, reg_base + 0x50, val); - rkvpss_hw_write(hw, reg_base + 0x20, ofl->unite_params[idx].y_w_phase); - rkvpss_hw_write(hw, reg_base + 0x24, ofl->unite_params[idx].c_w_phase); - val = cfg->scl_width / 2 - ALIGN_DOWN(cfg->scl_width / 2, 16); - rkvpss_hw_write(hw, reg_base + 0x48, val); - rkvpss_hw_write(hw, reg_base + 0x4c, val); - val = (cfg->crop_width / 2 + ofl->unite_right_enlarge) | - (cfg->crop_height << 16); - rkvpss_hw_write(hw, reg_base + 0x8, val); - val = cfg->scl_width / 2 | (cfg->scl_height << 16); - rkvpss_hw_write(hw, reg_base + 0xc, val); - ctrl |= RKVPSS_SCL_CLIP_EN | RKVPSS_SCL_IN_CLIP_EN; + memset(buf, 0, sizeof(buf)); + sprintf(buf, "sequence:%d\n", sequence); + kernel_write(filep, buf, strlen(buf), &pos); + if (pos < 0) + v4l2_err(&ofl->v4l2_dev, "Write data to %s failed\n", + rkvpss_regfile); + + for (i = 0; i < size; i = i + 16) { + memset(buf, 0, sizeof(buf)); + sprintf(buf, "%04x:", i); + kernel_write(filep, buf, strlen(buf), &pos); + if (pos < 0) + v4l2_err(&ofl->v4l2_dev, "Write data to %s failed\n", + rkvpss_regfile); + + for (j = 0; j < 16; j = j + 4) { + memset(buf, 0, sizeof(buf)); + sprintf(buf, " %08x", rkvpss_hw_read(hw, i + j)); + kernel_write(filep, buf, strlen(buf), &pos); + if (pos < 0) + v4l2_err(&ofl->v4l2_dev, "Write data to %s failed\n", + rkvpss_regfile); + } + memset(buf, 0, sizeof(buf)); + sprintf(buf, "%s\n", ""); + kernel_write(filep, buf, strlen(buf), &pos); + if (pos < 0) { + v4l2_err(&ofl->v4l2_dev, "Write data to %s failed\n", + rkvpss_regfile); + } } - if (cfg->scl_width != frame_cfg->input.width) { - rkvpss_hw_write(hw, reg_base + 0x10, ofl->unite_params[idx].y_w_fac); - rkvpss_hw_write(hw, reg_base + 0x14, ofl->unite_params[idx].c_w_fac); - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - if (cfg->scl_height != frame_cfg->input.height) { - rkvpss_hw_write(hw, reg_base + 0x18, ofl->unite_params[idx].y_h_fac); - rkvpss_hw_write(hw, reg_base + 0x1c, ofl->unite_params[idx].c_h_fac); - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; - } - } - -end: - rkvpss_hw_write(hw, reg_base, ctrl); - val = RKVPSS_SCL_GEN_UPD | RKVPSS_SCL_FORCE_UPD; - rkvpss_hw_write(hw, reg_base + 0x4, val); - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s unite:%d left:%d hw ch:%d ctrl:0x%x y_src_size:0x%x y_dst_size:0x%x\n", - __func__, unite, left, idx, - rkvpss_hw_read(hw, reg_base), - rkvpss_hw_read(hw, reg_base + 0x8), - rkvpss_hw_read(hw, reg_base + 0xc)); -} - -static void scale_config(struct file *file, - struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - int i; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - - if (i == RKVPSS_OUTPUT_CH0) - poly_phase_scale(cfg, ofl, &cfg->output[i], unite, left); - else - bilinear_scale(cfg, ofl, &cfg->output[i], i, unite, left); + filp_close(filep, NULL); } } -static int cmsc_config(struct rkvpss_offline_dev *ofl, - struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_hw_dev *hw = ofl->hw; - struct rkvpss_cmsc_cfg *cmsc_cfg, tmp_cfg = {0}; - struct rkvpss_cmsc_win *win; - struct rkvpss_cmsc_point *point; - int i, j, k; - u32 ch_win_en[RKVPSS_OUTPUT_MAX]; - u32 ch_win_mode[RKVPSS_OUTPUT_MAX]; - u32 win_color[RKVPSS_CMSC_WIN_MAX]; - u32 val, slope, hor, mask, mosaic_block = 0, ctrl = 0; - u32 hw_in_w, hw_in_h; - - if (!hw->is_ofl_cmsc) - return 0; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - ch_win_en[i] = 0; - ch_win_mode[i] = 0; - cmsc_cfg = &cfg->output[i].cmsc; - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - if (i == 0) - win_color[j] = 0; - if (!cmsc_cfg->win[j].win_en) - continue; - ch_win_en[i] |= BIT(j); - ch_win_mode[i] |= cmsc_cfg->win[j].mode ? BIT(j) : 0; - /** mosaic_block use the last channel **/ - if (cmsc_cfg->win[j].mode) - mosaic_block = cfg->output[i].cmsc.mosaic_block; - /** window cover all channel consistent **/ - if (!cfg->output[i].cmsc.win[j].mode) { - win_color[j] = RKVPSS_CMSK_WIN_YUV(cfg->output[i].cmsc.win[j].cover_color_y, - cfg->output[i].cmsc.win[j].cover_color_u, - cfg->output[i].cmsc.win[j].cover_color_v); - if (cfg->output[i].cmsc.win[j].cover_color_a > 15) - cfg->output[i].cmsc.win[j].cover_color_a = 15; - win_color[j] |= RKVPSS_CMSC_WIN_ALPHA(cfg->output[i].cmsc.win[j].cover_color_a); - } - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) - tmp_cfg.win[j].point[k] = cmsc_cfg->win[j].point[k]; - } - } - - /* deal unite left params */ - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!unite || !left) - break; - if (!cfg->output[i].enable) - continue; - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - win = &tmp_cfg.win[j]; - if (!(ch_win_en[i] & BIT(j))) - continue; - mask = 0; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - point = &win->point[k]; - if (point->x >= cfg->input.width / 2) - mask |= BIT(k); - else - mask &= ~BIT(k); - } - if (mask == 0xf) { - /** all right **/ - ch_win_en[i] &= ~BIT(j); - } else if (mask != 0) { - /** middle need avoid pentagon **/ - if (win->point[0].x != win->point[3].x || - win->point[1].x != win->point[2].x) { - ch_win_en[i] &= ~BIT(j); - } else { - win->point[1].x = cfg->input.width / 2; - win->point[2].x = cfg->input.width / 2; - } - } - } - } - - /* deal unite right params */ - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!unite || left) - break; - if (!cfg->output[i].enable) - continue; - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - win = &tmp_cfg.win[j]; - if (!(ch_win_en[i] & BIT(j))) - continue; - mask = 0; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - point = &win->point[k]; - if (point->x <= cfg->input.width / 2) - mask |= BIT(k); - else - mask &= ~BIT(k); - } - if (mask == 0xf) { - /** all left **/ - ch_win_en[i] &= ~BIT(j); - } else if (mask != 0) { - /** middle need avoid pentagon **/ - if (win->point[0].x != win->point[3].x || - win->point[1].x != win->point[2].x) { - ch_win_en[i] &= ~BIT(j); - } else { - win->point[0].x = ofl->unite_right_enlarge; - win->point[3].x = ofl->unite_right_enlarge; - win->point[1].x = win->point[1].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - win->point[2].x = win->point[2].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - } - } else { - /** all right **/ - win->point[0].x = win->point[0].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - win->point[1].x = win->point[1].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - win->point[2].x = win->point[2].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - win->point[3].x = win->point[3].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - } - } - } - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - if (ch_win_en[i]) { - ctrl |= RKVPSS_CMSC_EN; - ctrl |= RKVPSS_CMSC_CHN_EN(i); - } - rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_WIN + i * 4, ch_win_en[i]); - rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_MODE + i * 4, ch_win_mode[i]); - hw_in_w = rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_WIDTH); - hw_in_h = rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_HEIGHT); - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - if (!(ch_win_en[i] & BIT(j))) - continue; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - if (tmp_cfg.win[j].point[k].x > hw_in_w || - tmp_cfg.win[j].point[k].y > hw_in_h) { - v4l2_err(&ofl->v4l2_dev, - "%s cmsc coordinate error dev_id:%d unite:%u left:%u ch:%d win:%d point:%d x:%u y:%u hw_in_w:%u hw_in_h:%u\n", - __func__, cfg->dev_id, unite, left, - i, j, k, - tmp_cfg.win[j].point[k].x, - tmp_cfg.win[j].point[k].y, - hw_in_w, hw_in_w); - return -EINVAL; - } - val = RKVPSS_CMSC_WIN_VTX(tmp_cfg.win[j].point[k].x, - tmp_cfg.win[j].point[k].y); - rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val); - rkvpss_cmsc_slop(&tmp_cfg.win[j].point[k], - (k + 1 == RKVPSS_CMSC_POINT_MAX) ? - &tmp_cfg.win[j].point[0] : &tmp_cfg.win[j].point[k + 1], - &slope, &hor); - val = RKVPSS_CMSC_WIN_SLP(slope, hor); - rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val); - v4l2_dbg(4, rkvpss_debug, &ofl->v4l2_dev, - "%s dev_id:%d, unite:%d left:%d ch:%d win:%d point:%d x:%u y:%u", - __func__, cfg->dev_id, unite, left, i, j, k, - tmp_cfg.win[j].point[k].x, - tmp_cfg.win[j].point[k].y); - } - if ((ch_win_mode[i] & BIT(j))) - continue; - rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_PARA + j * 4, win_color[j]); - } - } - - ctrl |= RKVPSS_CMSC_BLK_SZIE(mosaic_block); - rkvpss_hw_write(hw, RKVPSS_CMSC_CTRL, ctrl); - - val = RKVPSS_CMSC_GEN_UPD | RKVPSS_CMSC_FORCE_UPD; - rkvpss_hw_write(hw, RKVPSS_CMSC_UPDATE, val); - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s dev_id:%d, unite:%d left:%d hw ctrl:0x%x update_val:0x%x", - __func__, cfg->dev_id, unite, left, ctrl, val); - - return 0; -} - -static void aspt_config(struct file *file, - struct rkvpss_frame_cfg *cfg) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - u32 reg_base, val; - int i; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - - switch (i) { - case RKVPSS_OUTPUT_CH0: - reg_base = RKVPSS_RATIO0_BASE; - break; - case RKVPSS_OUTPUT_CH1: - reg_base = RKVPSS_RATIO1_BASE; - break; - case RKVPSS_OUTPUT_CH2: - reg_base = RKVPSS_RATIO2_BASE; - break; - case RKVPSS_OUTPUT_CH3: - reg_base = RKVPSS_RATIO3_BASE; - break; - default: - return; - } - - if (!cfg->output[i].aspt.enable) { - rkvpss_hw_write(hw, reg_base, 0); - val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; - rkvpss_hw_write(hw, reg_base + 0x4, val); - continue; - } - val = cfg->output[i].scl_width | (cfg->output[i].scl_height << 16); - rkvpss_hw_write(hw, reg_base + 0x10, val); - val = cfg->output[i].aspt.width | (cfg->output[i].aspt.height << 16); - rkvpss_hw_write(hw, reg_base + 0x14, val); - val = cfg->output[i].aspt.h_offs | (cfg->output[i].aspt.v_offs << 16); - rkvpss_hw_write(hw, reg_base + 0x18, val); - val = cfg->output[i].aspt.color_y | - (cfg->output[i].aspt.color_u << 16) | - (cfg->output[i].aspt.color_v << 24); - rkvpss_hw_write(hw, reg_base + 0x1c, val); - rkvpss_hw_write(hw, reg_base, RKVPSS_RATIO_EN); - val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; - rkvpss_hw_write(hw, reg_base + 0x4, val); - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s hw ch:%d ctrl:0x%x in_size:0x%x out_size:0x%x offset:0x%x\n", - __func__, i, - rkvpss_hw_read(hw, reg_base), - rkvpss_hw_read(hw, reg_base + 0x10), - rkvpss_hw_read(hw, reg_base + 0x14), - rkvpss_hw_read(hw, reg_base + 0x18)); - } -} - -static void add_cfginfo(struct rkvpss_offline_dev *ofl, struct rkvpss_frame_cfg *cfg) -{ - struct rkvpss_ofl_cfginfo *cfginfo = NULL, *new_cfg = NULL, *first_cfg = NULL; - int i, count = 0; - - new_cfg = kzalloc(sizeof(struct rkvpss_ofl_cfginfo), GFP_KERNEL); - new_cfg->dev_id = cfg->dev_id; - new_cfg->sequence = cfg->sequence; - new_cfg->input.buf_fd = cfg->input.buf_fd; - new_cfg->input.format = cfg->input.format; - new_cfg->input.width = cfg->input.width; - new_cfg->input.height = cfg->input.height; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - new_cfg->output[i].enable = cfg->output[i].enable; - new_cfg->output[i].buf_fd = cfg->output[i].buf_fd; - new_cfg->output[i].format = cfg->output[i].format; - new_cfg->output[i].crop_v_offs = cfg->output[i].crop_v_offs; - new_cfg->output[i].crop_h_offs = cfg->output[i].crop_h_offs; - new_cfg->output[i].crop_width = cfg->output[i].crop_width; - new_cfg->output[i].crop_height = cfg->output[i].crop_height; - new_cfg->output[i].scl_width = cfg->output[i].scl_width; - new_cfg->output[i].scl_height = cfg->output[i].scl_height; - } - - mutex_lock(&ofl->ofl_lock); - list_for_each_entry(cfginfo, &ofl->cfginfo_list, list) { - count++; - } - while (count >= rkvpss_cfginfo_num && count != 0) { - first_cfg = list_first_entry(&ofl->cfginfo_list, struct rkvpss_ofl_cfginfo, list); - list_del_init(&first_cfg->list); - kfree(first_cfg); - count--; - } - if (rkvpss_cfginfo_num) - list_add_tail(&new_cfg->list, &ofl->cfginfo_list); - else - kfree(new_cfg); - mutex_unlock(&ofl->ofl_lock); -} - - -static int read_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - const struct vb2_mem_ops *mem_ops = hw->mem_ops; - struct sg_table *sg_tbl; - struct rkvpss_offline_buf *buf; - u32 in_ctrl, in_size, in_c_offs, unite_r_offs, val, mask, unite_off = 0, enlarge = 0; - - in_c_offs = 0; - in_ctrl = 0; - switch (cfg->input.format) { - case V4L2_PIX_FMT_NV16: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = cfg->input.stride * cfg->input.height; - in_size = cfg->input.stride * cfg->input.height * 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; - unite_off = 8; - break; - case V4L2_PIX_FMT_NV12: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = cfg->input.stride * cfg->input.height; - in_size = cfg->input.stride * cfg->input.height * 3 / 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; - unite_off = 8; - break; - case V4L2_PIX_FMT_NV61: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = cfg->input.stride * cfg->input.height; - in_size = cfg->input.stride * cfg->input.height * 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_422SP | RKVPSS_MI_RD_UV_SWAP; - unite_off = 8; - break; - case V4L2_PIX_FMT_NV21: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = cfg->input.stride * cfg->input.height; - in_size = cfg->input.stride * cfg->input.height * 3 / 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_420SP | RKVPSS_MI_RD_UV_SWAP; - unite_off = 8; - break; - case V4L2_PIX_FMT_RGB565: - if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 2, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565; - unite_off = 16; - break; - case V4L2_PIX_FMT_RGB565X: - if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 2, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565 | RKVPSS_MI_RD_RB_SWAP; - unite_off = 16; - break; - case V4L2_PIX_FMT_RGB24: - if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 3, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888; - unite_off = 24; - break; - case V4L2_PIX_FMT_BGR24: - if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 3, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888 | RKVPSS_MI_RD_RB_SWAP; - unite_off = 24; - break; - case V4L2_PIX_FMT_XRGB32: - if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 4, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP; - unite_off = 32; - break; - case V4L2_PIX_FMT_XBGR32: - if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 4, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP; - unite_off = 32; - break; - case V4L2_PIX_FMT_RGBX32: - if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 4, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 - | RKVPSS_MI_RD_RB_SWAP - | RKVPSS_MI_RD_ALPHA_SWAP; - unite_off = 32; - break; - case V4L2_PIX_FMT_BGRX32: - if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 4, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 - | RKVPSS_MI_RD_RB_SWAP - | RKVPSS_MI_RD_ALPHA_SWAP; - unite_off = 32; - break; - case V4L2_PIX_FMT_FBC0: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = 0; - in_size = cfg->input.stride * cfg->input.height * 3 / 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; - break; - case V4L2_PIX_FMT_FBC2: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = 0; - in_size = cfg->input.stride * cfg->input.height * 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; - break; - case V4L2_PIX_FMT_FBC4: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = 0; - in_size = cfg->input.stride * cfg->input.height * 3; - in_ctrl |= RKVPSS_MI_RD_INPUT_422SP | RKVPSS_MI_RD_FBCD_YUV444_EN; - break; - case V4L2_PIX_FMT_TILE420: - if (cfg->input.stride < ALIGN(cfg->input.width * 6, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 6, 16); - in_c_offs = 0; - in_size = cfg->input.stride * (cfg->input.height / 4); - in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; - break; - case V4L2_PIX_FMT_TILE422: - if (cfg->input.stride < ALIGN(cfg->input.width * 8, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 8, 16); - in_c_offs = 0; - in_size = cfg->input.stride * (cfg->input.height / 4); - in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; - break; - default: - v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support input format:%c%c%c%c\n", - cfg->dev_id, cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24); - return -EINVAL; - } - - buf = buf_add(file, cfg->dev_id, cfg->input.buf_fd, in_size); - if (!buf) - return -ENOMEM; - - sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); - - if (!unite) { - val = cfg->input.width; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); - val = cfg->input.height; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); - val = sg_dma_address(sg_tbl->sgl); - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); - val += in_c_offs; - rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); - } else { - val = cfg->input.height; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); - ofl->unite_right_enlarge = ALIGN(cfg->input.width / 2, 16) - - (cfg->input.width / 2) + 16; - - if (left) { - if (!cfg->mirror) - enlarge = UNITE_LEFT_ENLARGE; - else - enlarge = ofl->unite_right_enlarge; - val = cfg->input.width / 2 + enlarge; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); - val = sg_dma_address(sg_tbl->sgl); - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); - val += in_c_offs; - rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); - } else { - if (!cfg->mirror) - enlarge = ofl->unite_right_enlarge; - else - enlarge = UNITE_LEFT_ENLARGE; - val = cfg->input.width / 2 + enlarge; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); - val = (cfg->input.width / 2 - enlarge) * unite_off; - unite_r_offs = ALIGN_DOWN(val / 8, 16); - val = sg_dma_address(sg_tbl->sgl) + unite_r_offs; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); - val += in_c_offs; - rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); - } - } - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s unite:%d left:%d hw width:%d height:%d y_base:0x%x\n", - __func__, unite, left, - rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_WIDTH), - rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_HEIGHT), - rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_BASE)); - - if (cfg->input.format == V4L2_PIX_FMT_FBC0 || - cfg->input.format == V4L2_PIX_FMT_FBC2 || - cfg->input.format == V4L2_PIX_FMT_FBC4) { - in_ctrl |= RKVPSS_MI_RD_MODE(2) | RKVPSS_MI_RD_FBCD_OPT_DIS; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, 0); - } else { - if (cfg->input.format == V4L2_PIX_FMT_TILE420 || - cfg->input.format == V4L2_PIX_FMT_TILE422) { - in_ctrl |= RKVPSS_MI_RD_MODE(1); - switch (cfg->input.rotate) { - case ROTATE_90: - in_ctrl |= RKVPSS_MI_RD_ROT_90; - break; - case ROTATE_180: - in_ctrl |= RKVPSS_MI_RD_ROT_180; - break; - case ROTATE_270: - in_ctrl |= RKVPSS_MI_RD_ROT_270; - break; - default: - in_ctrl |= RKVPSS_MI_RD_ROT_0; - break; - } - } - val = cfg->input.stride; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, val); - } - - mask = RKVPSS_MI_RD_GROUP_MODE(3) | RKVPSS_MI_RD_BURST16_LEN; - rkvpss_hw_set_bits(hw, RKVPSS_MI_RD_CTRL, ~mask, in_ctrl); - rkvpss_hw_write(hw, RKVPSS_MI_RD_INIT, RKVPSS_MI_RD_FORCE_UPD); - - return 0; -} - -static void crop_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - int i; - u32 reg, val, crop_en; - - crop_en = 0; - if (!unite) { - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - reg = RKVPSS_CROP0_0_H_OFFS; - val = cfg->output[i].crop_h_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_OFFS; - val = cfg->output[i].crop_v_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_H_SIZE; - val = cfg->output[i].crop_width; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_SIZE; - val = cfg->output[i].crop_height; - rkvpss_hw_write(hw, reg + i * 0x10, val); - crop_en |= RKVPSS_CROP_CHN_EN(i); - } - } else { - if (left) { - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - - reg = RKVPSS_CROP0_0_H_OFFS; - val = cfg->output[i].crop_h_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_OFFS; - val = cfg->output[i].crop_v_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_H_SIZE; - /*if no scale, left don't enlarge*/ - if (cfg->output[i].crop_width == cfg->output[i].scl_width) - val = cfg->output[i].crop_width / 2; - else - val = cfg->output[i].crop_width / 2 + UNITE_LEFT_ENLARGE; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_SIZE; - val = cfg->output[i].crop_height; - rkvpss_hw_write(hw, reg + i * 0x10, val); - crop_en |= RKVPSS_CROP_CHN_EN(i); - } - } else { - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - reg = RKVPSS_CROP0_0_H_OFFS; - val = ofl->unite_params[i].quad_crop_w; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_OFFS; - val = cfg->output[i].crop_v_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_H_SIZE; - val = cfg->output[i].crop_width / 2 + - ofl->unite_right_enlarge - - ofl->unite_params[i].quad_crop_w; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_SIZE; - val = cfg->output[i].crop_height; - rkvpss_hw_write(hw, reg + i * 0x10, val); - crop_en |= RKVPSS_CROP_CHN_EN(i); - } - } - } - rkvpss_hw_write(hw, RKVPSS_CROP0_CTRL, crop_en); - rkvpss_hw_write(hw, RKVPSS_CROP0_UPDATE, RKVPSS_CROP_FORCE_UPD); - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s unite:%d left:%d hw ch:%d h_offs:%d v_offs:%d width:%d height:%d\n", - __func__, unite, left, i, - rkvpss_hw_read(hw, RKVPSS_CROP0_0_H_OFFS + i * 0x10), - rkvpss_hw_read(hw, RKVPSS_CROP0_0_V_OFFS + i * 0x10), - rkvpss_hw_read(hw, RKVPSS_CROP0_0_H_SIZE + i * 0x10), - rkvpss_hw_read(hw, RKVPSS_CROP0_0_V_SIZE + i * 0x10)); - } -} - -static int write_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - const struct vb2_mem_ops *mem_ops = hw->mem_ops; - struct sg_table *sg_tbl; - struct rkvpss_offline_buf *buf; - struct rkvpss_output_ch out_ch[RKVPSS_OUTPUT_MAX] = { 0 }; - int i; - u32 w, h, val, reg, mask, mi_update, flip_en, unite_off = 0; - bool ch_en = false, wr_uv_swap = false; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!hw->is_ofl_ch[i] && cfg->output[i].enable) { - v4l2_err(&ofl->v4l2_dev, - "dev_id:%d ch%d no select for offline mode, set to disable\n", - cfg->dev_id, i); - cfg->output[i].enable = 0; - } - - if (!cfg->output[i].enable) - continue; - ch_en = true; - - if (cfg->output[i].aspt.enable) { - w = cfg->output[i].aspt.width; - h = cfg->output[i].aspt.height; - } else { - w = cfg->output[i].scl_width; - h = cfg->output[i].scl_height; - } - if (i == RKVPSS_OUTPUT_CH1) { - bool is_fmt_find = true; - - switch (cfg->output[i].format) { - case V4L2_PIX_FMT_RGB565: - if (cfg->output[i].stride < ALIGN(w * 2, 16)) - cfg->output[i].stride = ALIGN(w * 2, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB565 | RKVPSS_MI_CHN_WR_RB_SWAP; - break; - case V4L2_PIX_FMT_RGB24: - if (cfg->output[i].stride < ALIGN(w * 3, 16)) - cfg->output[i].stride = ALIGN(w * 3, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB888 | RKVPSS_MI_CHN_WR_RB_SWAP; - break; - case V4L2_PIX_FMT_RGB565X: - if (cfg->output[i].stride < ALIGN(w * 2, 16)) - cfg->output[i].stride = ALIGN(w * 2, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB565; - break; - case V4L2_PIX_FMT_BGR24: - if (cfg->output[i].stride < ALIGN(w * 3, 16)) - cfg->output[i].stride = ALIGN(w * 3, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB888; - break; - case V4L2_PIX_FMT_XBGR32: - if (cfg->output[i].stride < ALIGN(w * 4, 16)) - cfg->output[i].stride = ALIGN(w * 4, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_ARGB888; - break; - case V4L2_PIX_FMT_XRGB32: - if (cfg->output[i].stride < ALIGN(w * 4, 16)) - cfg->output[i].stride = ALIGN(w * 4, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_ARGB888 - | RKVPSS_MI_CHN_WR_RB_SWAP; - break; - default: - is_fmt_find = false; - } - if (is_fmt_find) { - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; - out_ch[i].size = cfg->output[i].stride * h; - continue; - } - } - switch (cfg->output[i].format) { - case V4L2_PIX_FMT_UYVY: - if (cfg->output[i].stride < ALIGN(w * 2, 16)) - cfg->output[i].stride = ALIGN(w * 2, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_422P | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; - out_ch[i].size = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_NV16: - if (cfg->output[i].stride < ALIGN(w, 16)) - cfg->output[i].stride = ALIGN(w, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; - out_ch[i].size = cfg->output[i].stride * h * 2; - out_ch[i].c_offs = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_NV12: - if (cfg->output[i].stride < ALIGN(w, 16)) - cfg->output[i].stride = ALIGN(w, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV420; - out_ch[i].size = cfg->output[i].stride * h * 3 / 2; - out_ch[i].c_offs = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_GREY: - if (cfg->output[i].stride < ALIGN(w, 16)) - cfg->output[i].stride = ALIGN(w, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV400; - out_ch[i].size = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_VYUY: - if (cfg->output[i].stride < ALIGN(w * 2, 16)) - cfg->output[i].stride = ALIGN(w * 2, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_422P | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; - out_ch[i].size = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_NV61: - if (cfg->output[i].stride < ALIGN(w, 16)) - cfg->output[i].stride = ALIGN(w, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; - out_ch[i].size = cfg->output[i].stride * h * 2; - out_ch[i].c_offs = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_NV21: - if (cfg->output[i].stride < ALIGN(w, 16)) - cfg->output[i].stride = ALIGN(w, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV420; - out_ch[i].size = cfg->output[i].stride * h * 3 / 2; - out_ch[i].c_offs = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_TILE420: - if (cfg->output[i].stride < ALIGN(w * 6, 16)) - cfg->output[i].stride = ALIGN(w * 6, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_YUV420; - out_ch[i].size = cfg->output[i].stride * (h / 4); - out_ch[i].c_offs = 0; - break; - case V4L2_PIX_FMT_TILE422: - if (cfg->output[i].stride < ALIGN(w * 8, 16)) - cfg->output[i].stride = ALIGN(w * 8, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_YUV422; - out_ch[i].size = cfg->output[i].stride * (h / 4); - out_ch[i].c_offs = 0; - break; - default: - v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support output ch%d format:%c%c%c%c\n", - cfg->dev_id, i, - cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - return -EINVAL; - } - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; - } - if (!ch_en) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d no output channel enable\n", cfg->dev_id); - return -EINVAL; - } - - mi_update = 0; - flip_en = 0; - mask = 0; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (hw->is_ofl_ch[i]) - mask |= RKVPSS_MI_CHN_V_FLIP(i); - if (!cfg->output[i].enable) - continue; - buf = buf_add(file, cfg->dev_id, cfg->output[i].buf_fd, out_ch[i].size); - if (!buf) - goto free_buf; - - if (unite && !left) - unite_off = (ALIGN_DOWN(cfg->output[i].scl_width / 2, 16) * 8) / 8; - - if (cfg->output[i].aspt.enable) - h = cfg->output[i].aspt.height; - else - h = cfg->output[i].scl_height; - - sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); - val = sg_dma_address(sg_tbl->sgl) + unite_off; - reg = RKVPSS_MI_CHN0_WR_Y_BASE; - rkvpss_hw_write(hw, reg + i * 0x100, val); - reg = RKVPSS_MI_CHN0_WR_CB_BASE; - val += out_ch[i].c_offs; - rkvpss_hw_write(hw, reg + i * 0x100, val); - - reg = RKVPSS_MI_CHN0_WR_Y_STRIDE; - val = cfg->output[i].stride; - rkvpss_hw_write(hw, reg + i * 0x100, val); - reg = RKVPSS_MI_CHN0_WR_Y_SIZE; - - if (cfg->output[i].format == V4L2_PIX_FMT_TILE420 || - cfg->output[i].format == V4L2_PIX_FMT_TILE422) - val = cfg->output[i].stride * (ALIGN(h, 4) / 4); - else - val = cfg->output[i].stride * h; - rkvpss_hw_write(hw, reg + i * 0x100, val); - reg = RKVPSS_MI_CHN0_WR_CB_SIZE; - val = out_ch[i].size - val; - rkvpss_hw_write(hw, reg + i * 0x100, val); - - reg = RKVPSS_MI_CHN0_WR_CTRL; - val = out_ch[i].ctrl; - rkvpss_hw_write(hw, reg + i * 0x100, val); - - if (cfg->output[i].flip && - !(cfg->output[i].format == V4L2_PIX_FMT_TILE420) && - !(cfg->output[i].format == V4L2_PIX_FMT_TILE422)) { - flip_en |= RKVPSS_MI_CHN_V_FLIP(i); - - switch (cfg->output[i].format) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - val = cfg->output[i].stride / 2; - break; - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_XRGB32: - val = cfg->output[i].stride / 4; - break; - default: - val = cfg->output[i].stride; - break; - } - val = val * h; - reg = RKVPSS_MI_CHN0_WR_Y_PIC_SIZE; - rkvpss_hw_write(hw, reg + i * 0x100, val); - } - mi_update |= (RKVPSS_MI_CHN0_FORCE_UPD << i); - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s unite:%d left:%d hw ch:%d y_size:%d y_stride:%d y_pic_size:%d y_base:0x%x", - __func__, unite, left, i, - rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_SIZE + i * 100), - rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_STRIDE + i * 100), - rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_PIC_SIZE + i * 100), - rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_BASE + i * 100)); - } - - if (flip_en) - rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_VFLIP_CTRL, mask, flip_en); - - /* config output uv swap */ - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (cfg->output[i].enable && - (cfg->output[i].format == V4L2_PIX_FMT_VYUY || - cfg->output[i].format == V4L2_PIX_FMT_NV21 || - cfg->output[i].format == V4L2_PIX_FMT_NV61)) - wr_uv_swap = true; - } - if (wr_uv_swap) { - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (cfg->output[i].enable && (cfg->output[i].format == V4L2_PIX_FMT_UYVY || - cfg->output[i].format == V4L2_PIX_FMT_NV12 || - cfg->output[i].format == V4L2_PIX_FMT_NV16)) { - v4l2_err(&ofl->v4l2_dev, - "dev_id:%d wr_uv_swap need to be consistent\n", - cfg->dev_id); - return -EAGAIN; - } - } - } - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (cfg->output[i].format == V4L2_PIX_FMT_VYUY || - cfg->output[i].format == V4L2_PIX_FMT_NV21 || - cfg->output[i].format == V4L2_PIX_FMT_NV61) { - mask = RKVPSS_MI_WR_UV_SWAP; - val = RKVPSS_MI_WR_UV_SWAP; - rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_CTRL, mask, val); - break; - } - } - - for (i = 0; i <= RKVPSS_OUTPUT_CH1; i++) { - if (cfg->output[i].format == V4L2_PIX_FMT_TILE420 || - cfg->output[i].format == V4L2_PIX_FMT_TILE422) { - mask = RKVPSS_MI_WR_TILE_SEL(3); - val = RKVPSS_MI_WR_TILE_SEL(i + 1); - rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_CTRL, mask, val); - } - } - - /* need update two for online2 mode */ - rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); - rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); - - return 0; - -free_buf: - for (i -= 1; i >= 0; i--) { - if (!cfg->output[i].enable) - continue; - buf_del(file, cfg->dev_id, cfg->output[i].buf_fd, false, true); - } - buf_del(file, cfg->dev_id, cfg->input.buf_fd, false, true); - return -ENOMEM; -} - -static void calc_unite_scl_params(struct file *file, struct rkvpss_frame_cfg *cfg) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_unite_scl_params *params; - int i; - u32 right_scl_need_size_y, right_scl_need_size_c; - u32 left_in_used_size_y, left_in_used_size_c; - u32 right_fst_position_y, right_fst_position_c; - u32 right_y_crop_total; - u32 right_c_crop_total; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (cfg->output[i].enable == 0) - continue; - params = &ofl->unite_params[i]; - params->y_w_fac = (cfg->output[i].crop_width - 1) * 4096 / - (cfg->output[i].scl_width - 1); - params->c_w_fac = (cfg->output[i].crop_width / 2 - 1) * 4096 / - (cfg->output[i].scl_width / 2 - 1); - params->y_h_fac = (cfg->output[i].crop_height - 1) * 4096 / - (cfg->output[i].scl_height - 1); - params->c_h_fac = (cfg->output[i].crop_height - 1) * 4096 / - (cfg->output[i].scl_height - 1); - - right_fst_position_y = cfg->output[i].scl_width / 2 * - params->y_w_fac; - right_fst_position_c = cfg->output[i].scl_width / 2 / 2 * - params->c_w_fac; - - left_in_used_size_y = right_fst_position_y >> 12; - left_in_used_size_c = (right_fst_position_c >> 12) * 2; - - params->y_w_phase = right_fst_position_y & 0xfff; - params->c_w_phase = right_fst_position_c & 0xfff; - - right_scl_need_size_y = cfg->output[i].crop_width - - left_in_used_size_y; - params->right_scl_need_size_y = right_scl_need_size_y; - right_scl_need_size_c = cfg->output[i].crop_width - - left_in_used_size_c; - params->right_scl_need_size_c = right_scl_need_size_c; - - if (i == 0 && cfg->output[i].crop_width != cfg->output[i].scl_width) { - right_y_crop_total = cfg->output[i].crop_width / 2 + - ofl->unite_right_enlarge - - right_scl_need_size_y - 3; - right_c_crop_total = cfg->output[i].crop_width / 2 + - ofl->unite_right_enlarge - - right_scl_need_size_c - 6; - } else { - right_y_crop_total = cfg->output[i].crop_width / 2 + - ofl->unite_right_enlarge - - right_scl_need_size_y; - right_c_crop_total = cfg->output[i].crop_width / 2 + - ofl->unite_right_enlarge - - right_scl_need_size_c; - } - - params->quad_crop_w = ALIGN_DOWN(min(right_y_crop_total, right_c_crop_total), 2); - - params->scl_in_crop_w_y = right_y_crop_total - params->quad_crop_w; - params->scl_in_crop_w_c = right_c_crop_total - params->quad_crop_w; - - if (rkvpss_debug >= 4) { - v4l2_info(&ofl->v4l2_dev, - "%s dev_id:%d seq:%d ch:%d y_w_fac:%u c_w_fac:%u y_h_fac:%u c_h_fac:%u\n", - __func__, cfg->dev_id, cfg->sequence, i, params->y_w_fac, - params->c_w_fac, params->y_h_fac, params->c_h_fac); - v4l2_info(&ofl->v4l2_dev, - "\t\t\t\t\t\t unite_right_enlarge:%u", - ofl->unite_right_enlarge); - v4l2_info(&ofl->v4l2_dev, - "\t\t\t\t\t\t y_w_phase:%u c_w_phase:%u quad_crop_w:%u scl_in_crop_w_y:%u scl_in_crop_w_c:%u\n", - params->y_w_phase, params->c_w_phase, - params->quad_crop_w, - params->scl_in_crop_w_y, params->scl_in_crop_w_c); - v4l2_info(&ofl->v4l2_dev, - "\t\t\t\t\t\t right_scl_need_size_y:%u right_scl_need_size_c:%u\n", - params->right_scl_need_size_y, - params->right_scl_need_size_c); - } - } -} - -static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - u32 update, val, mask; - int ret, i; - bool left_tmp; - - if (!unite || left) - add_cfginfo(ofl, cfg); - - init_completion(&ofl->cmpl); - ofl->mode_sel_en = false; - - ret = read_config(file, cfg, unite, left); - if (ret < 0) - return ret; - - if (unite && left) - calc_unite_scl_params(file, cfg); - - if (unite && cfg->mirror) - left_tmp = !left; - else - left_tmp = left; - - ret = cmsc_config(ofl, cfg, unite, left_tmp); - if (ret) - return ret; - crop_config(file, cfg, unite, left_tmp); - scale_config(file, cfg, unite, left_tmp); - if (!unite) - aspt_config(file, cfg); - ret = write_config(file, cfg, unite, left_tmp); - if (ret < 0) - return ret; - - mask = 0; - val = 0; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!hw->is_ofl_ch[i]) - continue; - mask |= (RKVPSS_ISP2VPSS_CHN0_SEL(3) << i * 2); - if (hw->is_ofl_cmsc) - mask |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN; - if (cfg->output[i].enable) - val |= (RKVPSS_ISP2VPSS_CHN0_SEL(1) << i * 2); - } - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_ONLINE, mask, val); - - update = 0; - mask = hw->is_ofl_cmsc ? RKVPSS_MIR_EN : 0; - val = (mask && cfg->mirror) ? RKVPSS_MIR_EN : 0; - if (mask) { - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CTRL, mask, val); - update |= RKVPSS_MIR_FORCE_UPD; - } - update |= RKVPSS_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD; - rkvpss_hw_write(hw, RKVPSS_VPSS_UPDATE, update); - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_IMSC, 0, RKVPSS_ALL_FRM_END); - - rkvpss_hw_write(hw, RKVPSS_MI_RD_START, RKVPSS_MI_RD_ST); - - ret = wait_for_completion_timeout(&ofl->cmpl, msecs_to_jiffies(500)); - if (!ret) { - v4l2_err(&ofl->v4l2_dev, "working timeout\n"); - ret = -EAGAIN; - } else { - ret = 0; - } - - return ret; -} - -static int rkvpss_module_get(struct file *file, - struct rkvpss_module_sel *get) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - int i, ret = 0; - - mutex_lock(&hw->dev_lock); - if (hw->is_ofl_cmsc) - get->mirror_cmsc_en = 1; - else - get->mirror_cmsc_en = 0; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (hw->is_ofl_ch[i]) - get->ch_en[i] = 1; - else - get->ch_en[i] = 0; - } - mutex_unlock(&hw->dev_lock); - - return ret; -} - -static int rkvpss_module_sel(struct file *file, - struct rkvpss_module_sel *sel) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - struct rkvpss_device *vpss; - int i, ret = 0; - - mutex_lock(&hw->dev_lock); - - if (!ofl->mode_sel_en) { - v4l2_err(&ofl->v4l2_dev, "already set module_sel\n"); - ret = -EINVAL; - goto unlock; - } - - for (i = 0; i < hw->dev_num; i++) { - vpss = hw->vpss[i]; - if (vpss && (vpss->vpss_sdev.state & VPSS_START)) { - v4l2_err(&ofl->v4l2_dev, "no support set mode when vpss working\n"); - ret = -EINVAL; - goto unlock; - } - } - - hw->is_ofl_cmsc = !!sel->mirror_cmsc_en; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) - hw->is_ofl_ch[i] = !!sel->ch_en[i]; -unlock: - mutex_unlock(&hw->dev_lock); - return ret; -} - -static int rkvpss_check_params(struct file *file, struct rkvpss_frame_cfg *cfg, bool *unite) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - int i, ret = 0, tile_num = 0; - - /* check dev id out of range */ - if (cfg->dev_id >= DEV_NUM_MAX) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d is out of range. range[0, %d]\n", - cfg->dev_id, DEV_NUM_MAX); - ret = -EINVAL; - goto end; - } - - /* set unite mode */ - if (cfg->input.width > RKVPSS_MAX_WIDTH) - *unite = true; - else - *unite = false; - - /* check input format */ - switch (cfg->input.format) { - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_BGR24: - case V4L2_PIX_FMT_XRGB32: - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_RGBX32: - case V4L2_PIX_FMT_BGRX32: - case V4L2_PIX_FMT_FBC0: - case V4L2_PIX_FMT_FBC2: - case V4L2_PIX_FMT_FBC4: - case V4L2_PIX_FMT_TILE420: - case V4L2_PIX_FMT_TILE422: - break; - default: - v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support input format:%c%c%c%c\n", - cfg->dev_id, cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24); - ret = -EINVAL; - goto end; - } - - /* check input size */ - if (cfg->input.width > RKVPSS_UNITE_MAX_WIDTH || - cfg->input.height > RKVPSS_UNITE_MAX_HEIGHT || - cfg->input.width < RKVPSS_MIN_WIDTH || - cfg->input.height < RKVPSS_MIN_HEIGHT) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d input size not support width:%d height:%d\n", - cfg->dev_id, cfg->input.width, cfg->input.height); - ret = -EINVAL; - goto end; - } - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - /* check output format */ - switch (cfg->output[i].format) { - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_VYUY: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_NV21: - break; - case V4L2_PIX_FMT_TILE420: - case V4L2_PIX_FMT_TILE422: - if (i == RKVPSS_OUTPUT_CH0 || i == RKVPSS_OUTPUT_CH1) { - tile_num++; - if (tile_num > 1) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d only ch0 or ch1 can tile write\n", - cfg->dev_id); - ret = -EINVAL; - goto end; - } - if (cfg->output[i].flip) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d tile write no support flip\n", - cfg->dev_id, i); - ret = -EINVAL; - goto end; - } - } else { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", - cfg->dev_id, i, cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - ret = -EINVAL; - goto end; - } - break; - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_BGR24: - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_XRGB32: - if (i != RKVPSS_OUTPUT_CH1) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", - cfg->dev_id, i, cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - ret = -EINVAL; - goto end; - } - break; - default: - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", - cfg->dev_id, i, cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - ret = -EINVAL; - goto end; - } - - /* check output size */ - if (cfg->output[i].scl_width > RKVPSS_UNITE_MAX_WIDTH || - cfg->output[i].scl_height > RKVPSS_UNITE_MAX_HEIGHT || - cfg->output[i].scl_width < RKVPSS_MIN_WIDTH || - cfg->output[i].scl_height < RKVPSS_MIN_HEIGHT) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d output size not support width:%d height:%d\n", - cfg->dev_id, cfg->output[i].scl_width, cfg->output[i].scl_height); - ret = -EINVAL; - goto end; - } - - /* check crop */ - cfg->output[i].crop_h_offs = ALIGN(cfg->output[i].crop_h_offs, 2); - cfg->output[i].crop_v_offs = ALIGN(cfg->output[i].crop_v_offs, 2); - cfg->output[i].crop_width = ALIGN(cfg->output[i].crop_width, 2); - cfg->output[i].crop_height = ALIGN(cfg->output[i].crop_height, 2); - if (!cfg->input.rotate || cfg->input.rotate == 2) { - if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > cfg->input.width) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d w:%d) input width:%d\n", - i, cfg->dev_id, cfg->output[i].crop_h_offs, - cfg->output[i].crop_width, cfg->input.width); - ret = -EINVAL; - goto end; - } - if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > cfg->input.height) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d h:%d) input height:%d\n", - cfg->dev_id, i, cfg->output[i].crop_v_offs, - cfg->output[i].crop_height, cfg->input.height); - ret = -EINVAL; - goto end; - } - } else { - if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > cfg->input.height) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d w:%d) input height:%d\n", - i, cfg->dev_id, cfg->output[i].crop_h_offs, - cfg->output[i].crop_width, cfg->input.height); - ret = -EINVAL; - goto end; - } - if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > cfg->input.width) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d h:%d) input width:%d\n", - cfg->dev_id, i, cfg->output[i].crop_v_offs, - cfg->output[i].crop_height, cfg->input.width); - ret = -EINVAL; - goto end; - } - } - if (*unite) { - if (cfg->output[i].crop_h_offs != (cfg->input.width - - (cfg->output[i].crop_h_offs + - cfg->output[i].crop_width))) { - v4l2_err(&ofl->v4l2_dev, " dev_id:%d ch%d unite crop_v need centered crop(h_offs:%d w:%d) input width:%d\n", - cfg->dev_id, i, cfg->output[i].crop_h_offs, - cfg->output[i].crop_width, cfg->input.width); - ret = -EINVAL; - goto end; - } - } - - /* check scale */ - if (i == RKVPSS_OUTPUT_CH2 || i == RKVPSS_OUTPUT_CH3) { - if (cfg->output[i].crop_width != cfg->output[i].scl_width && - cfg->output[i].crop_height != cfg->output[i].scl_height) { - if ((!*unite && cfg->output[i].scl_width > 1920) || - (*unite && cfg->output[i].scl_width > 1920 * 2)) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d single scale max width 1920\n", - cfg->dev_id, i); - ret = -EINVAL; - goto end; - } - } - } - } - - /* check rotate */ - switch (cfg->input.rotate) { - case ROTATE_90: - case ROTATE_180: - case ROTATE_270: - if (cfg->input.format != V4L2_PIX_FMT_TILE420 && - cfg->input.format != V4L2_PIX_FMT_TILE422) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d input format:%c%c%c%c not support rotate\n", - cfg->dev_id, cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24); - ret = -EINVAL; - goto end; - } - break; - default: - break; - } - - /** unite constraints **/ - if (*unite) { - if (cfg->input.format == V4L2_PIX_FMT_FBC0 || - cfg->input.format == V4L2_PIX_FMT_FBC2 || - cfg->input.format == V4L2_PIX_FMT_FBC4 || - cfg->input.format == V4L2_PIX_FMT_TILE420 || - cfg->input.format == V4L2_PIX_FMT_TILE422) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support input this format:%c%c%c%c\n", - cfg->dev_id, cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24); - ret = -EINVAL; - goto end; - } - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - if (cfg->output[i].format != V4L2_PIX_FMT_NV12 && - cfg->output[i].format != V4L2_PIX_FMT_NV16 && - cfg->output[i].format != V4L2_PIX_FMT_NV21 && - cfg->output[i].format != V4L2_PIX_FMT_NV61) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support output this format:%c%c%c%c\n", - cfg->dev_id, cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - ret = -EINVAL; - goto end; - } - if (cfg->output[i].scl_width > cfg->input.width) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite horizontal no support scale up\n", - cfg->dev_id); - ret = -EINVAL; - goto end; - } - if (cfg->output[i].aspt.enable) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support aspt\n", - cfg->dev_id); - ret = -EINVAL; - goto end; - } - } - } - -end: - return ret; -} - -static int rkvpss_prepare_run(struct file *file, struct rkvpss_frame_cfg *cfg) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - int ret = 0; - bool unite; - int i, j, k; - s64 us = 0; - u64 ns; - ktime_t t = 0; - - ns = ktime_get_ns(); - ofl->dev_rate[cfg->dev_id].in_rate = ns - ofl->dev_rate[cfg->dev_id].in_timestamp; - ofl->dev_rate[cfg->dev_id].in_timestamp = ns; - - ret = rkvpss_check_params(file, cfg, &unite); - if (ret < 0) - goto end; - - /******** show cfg info ********/ - if (rkvpss_debug >= 2) { - t = ktime_get(); - - v4l2_info(&ofl->v4l2_dev, - "%s dev_id:%d seq:%d mirror:%d input:%dx%d buffd:%d format:%c%c%c%c stride:%d rotate:%d\n", - __func__, cfg->dev_id, cfg->sequence, cfg->mirror, - cfg->input.width, cfg->input.height, cfg->input.buf_fd, - cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24, - cfg->input.stride, cfg->input.rotate); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - v4l2_info(&ofl->v4l2_dev, - "\t\t\tch%d enable:%d crop:(%d,%d)/%dx%d scl:%dx%d flip:%d buffd:%d format:%c%c%c%c stride:%d\n", - i, cfg->output[i].enable, - cfg->output[i].crop_h_offs, cfg->output[i].crop_v_offs, - cfg->output[i].crop_width, cfg->output[i].crop_height, - cfg->output[i].scl_width, cfg->output[i].scl_height, - cfg->output[i].flip, cfg->output[i].buf_fd, - cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24, - cfg->output[i].stride); - if (rkvpss_debug < 4) - break; - if (!cfg->output[i].enable) - continue; - v4l2_info(&ofl->v4l2_dev, - "\t\t\tcmsc mosaic_block:%d width_ro:%d height_ro:%d\n", - cfg->output[i].cmsc.mosaic_block, - cfg->output[i].cmsc.width_ro, - cfg->output[i].cmsc.height_ro); - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - if (!cfg->output[i].cmsc.win[j].win_en) - continue; - v4l2_info(&ofl->v4l2_dev, - "\t\t\t\twin:%d win_en:%u mode:%u color_y:%u color_u:%u color_v:%u color_a:%u\n", - j, - cfg->output[i].cmsc.win[j].win_en, - cfg->output[i].cmsc.win[j].mode, - cfg->output[i].cmsc.win[j].cover_color_y, - cfg->output[i].cmsc.win[j].cover_color_u, - cfg->output[i].cmsc.win[j].cover_color_v, - cfg->output[i].cmsc.win[j].cover_color_a); - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - v4l2_info(&ofl->v4l2_dev, - "\t\t\t\t\tpoint:%d x:%d y:%d\n", - k, - cfg->output[i].cmsc.win[j].point[k].x, - cfg->output[i].cmsc.win[j].point[k].y); - } - } - v4l2_info(&ofl->v4l2_dev, - "\t\t\taspt_en:%d w:%d h:%d h_offs:%d v_offs:%d color_y:%d color_u:%d color_v:%d\n", - cfg->output[i].aspt.enable, - cfg->output[i].aspt.width, - cfg->output[i].aspt.height, - cfg->output[i].aspt.h_offs, - cfg->output[i].aspt.v_offs, - cfg->output[i].aspt.color_y, - cfg->output[i].aspt.color_u, - cfg->output[i].aspt.color_v); - } - } - - if (!unite) { - ret = rkvpss_ofl_run(file, cfg, false, false); - if (ret < 0) - goto end; - } else { - ret = rkvpss_ofl_run(file, cfg, true, true); - if (ret < 0) { - v4l2_err(&ofl->v4l2_dev, "unite left error\n"); - goto end; - } - ret = rkvpss_ofl_run(file, cfg, true, false); - if (ret < 0) { - v4l2_err(&ofl->v4l2_dev, "unite right error\n"); - goto end; - } - } - - if (rkvpss_debug >= 2) { - us = ktime_us_delta(ktime_get(), t); - v4l2_info(&ofl->v4l2_dev, - "%s end, time:%lldus\n", __func__, us); - } - - ns = ktime_get_ns(); - ofl->dev_rate[cfg->dev_id].out_rate = ns - ofl->dev_rate[cfg->dev_id].out_timestamp; - ofl->dev_rate[cfg->dev_id].out_timestamp = ns; - ofl->dev_rate[cfg->dev_id].sequence = cfg->sequence; - ofl->dev_rate[cfg->dev_id].delay = ofl->dev_rate[cfg->dev_id].out_timestamp - - ofl->dev_rate[cfg->dev_id].in_timestamp; - -end: - return ret; -} - -static long rkvpss_ofl_ioctl(struct file *file, void *fh, - bool valid_prio, unsigned int cmd, void *arg) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - long ret = 0; - bool unite; - - ofl->pm_need_wait = true; - - if (!arg) { - ret = -EINVAL; - goto out; - } - - switch (cmd) { - case RKVPSS_CMD_MODULE_SEL: - ret = rkvpss_module_sel(file, arg); - break; - case RKVPSS_CMD_MODULE_GET: - ret = rkvpss_module_get(file, arg); - break; - case RKVPSS_CMD_FRAME_HANDLE: - ret = rkvpss_prepare_run(file, arg); - break; - case RKVPSS_CMD_BUF_ADD: - ret = rkvpss_ofl_buf_add(file, arg); - break; - case RKVPSS_CMD_BUF_DEL: - rkvpss_ofl_buf_del(file, arg); - break; - case RKVPSS_CMD_CHECKPARAMS: - ret = rkvpss_check_params(file, arg, &unite); - break; - default: - ret = -EFAULT; - } - -out: - /* notify hw suspend */ - if (ofl->hw->is_suspend) - complete(&ofl->pm_cmpl); - - ofl->pm_need_wait = false; - return ret; -} - -static const struct v4l2_ioctl_ops offline_ioctl_ops = { - .vidioc_default = rkvpss_ofl_ioctl, -}; - -static int ofl_open(struct file *file) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - int ret; - - ret = v4l2_fh_open(file); - if (ret) - goto end; - - mutex_lock(&ofl->hw->dev_lock); - ret = pm_runtime_get_sync(ofl->hw->dev); - mutex_unlock(&ofl->hw->dev_lock); - if (ret < 0) - v4l2_fh_release(file); -end: - v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, - "%s file:%p ret:%d\n", __func__, file, ret); - return (ret > 0) ? 0 : ret; -} - -static int ofl_release(struct file *file) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - - v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, - "%s file:%p\n", __func__, file); - - v4l2_fh_release(file); - buf_del(file, 0, 0, true, false); - mutex_lock(&ofl->hw->dev_lock); - pm_runtime_put_sync(ofl->hw->dev); - mutex_unlock(&ofl->hw->dev_lock); - return 0; -} - -static const struct v4l2_file_operations offline_fops = { - .owner = THIS_MODULE, - .open = ofl_open, - .release = ofl_release, - .unlocked_ioctl = video_ioctl2, -#ifdef CONFIG_COMPAT - .compat_ioctl32 = video_ioctl2, -#endif -}; - -static const struct video_device offline_videodev = { - .name = "rkvpss-offline", - .vfl_dir = VFL_DIR_RX, - .fops = &offline_fops, - .ioctl_ops = &offline_ioctl_ops, - .minor = -1, - .release = video_device_release_empty, -}; void rkvpss_offline_irq(struct rkvpss_hw_dev *hw, u32 irq) { - struct rkvpss_offline_dev *ofl = &hw->ofl_dev; - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s 0x%x\n", __func__, irq); - - if (!completion_done(&ofl->cmpl)) - complete(&ofl->cmpl); + if (hw->vpss_ver == VPSS_V10) + rkvpss_offline_irq_v1(hw, irq); } int rkvpss_register_offline(struct rkvpss_hw_dev *hw) { - struct rkvpss_offline_dev *ofl = &hw->ofl_dev; - struct v4l2_device *v4l2_dev; - struct video_device *vfd; - int ret; + int ret = -EINVAL; - ofl->hw = hw; - v4l2_dev = &ofl->v4l2_dev; - strscpy(v4l2_dev->name, offline_videodev.name, sizeof(v4l2_dev->name)); - ret = v4l2_device_register(hw->dev, v4l2_dev); - if (ret) - return ret; + if (hw->vpss_ver == VPSS_V10) + ret = rkvpss_register_offline_v1(hw); - mutex_init(&ofl->apilock); - ofl->vfd = offline_videodev; - ofl->mode_sel_en = true; - vfd = &ofl->vfd; - vfd->device_caps = V4L2_CAP_STREAMING; - vfd->lock = &ofl->apilock; - vfd->v4l2_dev = v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); - if (ret) { - v4l2_err(v4l2_dev, "Failed to register video device\n"); - goto unreg_v4l2; - } - video_set_drvdata(vfd, ofl); - INIT_LIST_HEAD(&ofl->list); - INIT_LIST_HEAD(&ofl->cfginfo_list); - mutex_init(&ofl->ofl_lock); - rkvpss_offline_proc_init(ofl); - ofl->pm_need_wait = false; - init_completion(&ofl->pm_cmpl); - return 0; -unreg_v4l2: - mutex_destroy(&ofl->apilock); - v4l2_device_unregister(v4l2_dev); return ret; } void rkvpss_unregister_offline(struct rkvpss_hw_dev *hw) { - mutex_destroy(&hw->ofl_dev.apilock); - video_unregister_device(&hw->ofl_dev.vfd); - v4l2_device_unregister(&hw->ofl_dev.v4l2_dev); - mutex_destroy(&hw->ofl_dev.ofl_lock); - rkvpss_offline_proc_cleanup(&hw->ofl_dev); + if (hw->vpss_ver == VPSS_V10) + rkvpss_unregister_offline_v1(hw); } + diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.h b/drivers/media/platform/rockchip/vpss/vpss_offline.h index 78b7434d6a9c..ad858e9e63e9 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.h +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.h @@ -7,7 +7,7 @@ #define UNITE_ENLARGE 16 #define UNITE_LEFT_ENLARGE 16 -#include "hw.h" +extern char rkvpss_regfile[RKVPSS_REGFILE_LEN]; struct rkvpss_ofl_incfginfo { int width; @@ -82,5 +82,6 @@ struct rkvpss_offline_dev { int rkvpss_register_offline(struct rkvpss_hw_dev *hw); void rkvpss_unregister_offline(struct rkvpss_hw_dev *hw); void rkvpss_offline_irq(struct rkvpss_hw_dev *hw, u32 irq); +void rkvpss_dump_reg(struct rkvpss_offline_dev *ofl, int sequence, int size); #endif diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_v1.c b/drivers/media/platform/rockchip/vpss/vpss_offline_v1.c new file mode 100644 index 000000000000..7ec40d3da13b --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_v1.c @@ -0,0 +1,2271 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "vpss.h" +#include "common.h" +#include "stream.h" +#include "dev.h" +#include "vpss_offline.h" +#include "hw.h" +#include "regs_v1.h" + +#include "procfs.h" +#include "vpss_offline_v1.h" + +struct rkvpss_output_ch { + u32 ctrl; + u32 size; + u32 c_offs; +}; + +struct rkvpss_offline_buf { + struct list_head list; + struct vb2_buffer vb; + struct vb2_queue vb2_queue; + struct file *file; + struct dma_buf *dbuf; + void *mem; + int dev_id; + int fd; + bool alloc; +}; + +static void init_vb2(struct rkvpss_offline_dev *ofl, + struct rkvpss_offline_buf *buf) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING; + + if (!buf) + return; + memset(&buf->vb, 0, sizeof(buf->vb)); + memset(&buf->vb2_queue, 0, sizeof(buf->vb2_queue)); + buf->vb2_queue.gfp_flags = GFP_KERNEL | GFP_DMA32; + buf->vb2_queue.dma_dir = DMA_BIDIRECTIONAL; + if (hw->is_dma_contig) + attrs |= DMA_ATTR_FORCE_CONTIGUOUS; + buf->vb2_queue.dma_attrs = attrs; + buf->vb.vb2_queue = &buf->vb2_queue; +} + +static void buf_del(struct file *file, int id, int fd, bool is_all, bool running) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *ops = hw->mem_ops; + struct rkvpss_offline_buf *buf, *next; + + mutex_lock(&hw->dev_lock); + list_for_each_entry_safe(buf, next, &ofl->list, list) { + if (buf->file == file && (is_all || buf->fd == fd)) { + if (!is_all && running && buf->alloc) + break; + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p dev_id:%d fd:%d dbuf:%p\n", + __func__, file, buf->dev_id, buf->fd, buf->dbuf); + if (!buf->alloc) { + ops->unmap_dmabuf(buf->mem); + ops->detach_dmabuf(buf->mem); + dma_buf_put(buf->dbuf); + } else { + dma_buf_put(buf->dbuf); + ops->put(buf->mem); + } + buf->file = NULL; + buf->mem = NULL; + buf->dbuf = NULL; + buf->fd = -1; + list_del(&buf->list); + kfree(buf); + if (!is_all) + break; + } + } + mutex_unlock(&hw->dev_lock); +} + +static struct rkvpss_offline_buf *buf_add(struct file *file, int id, int fd, int size) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *ops = hw->mem_ops; + struct rkvpss_offline_buf *buf = NULL, *next = NULL; + struct dma_buf *dbuf; + void *mem = NULL; + bool is_add = true; + + dbuf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dbuf)) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d invalid dmabuf fd:%d", id, fd); + return buf; + } + if (size && dbuf->size < size) { + v4l2_err(&ofl->v4l2_dev, + "dev_id:%d input fd:%d size error:%zu < %u\n", + id, fd, dbuf->size, size); + dma_buf_put(dbuf); + return buf; + } + + mutex_lock(&hw->dev_lock); + list_for_each_entry_safe(buf, next, &ofl->list, list) { + if (buf->file == file && buf->fd == fd && buf->dbuf == dbuf) { + is_add = false; + break; + } + } + + if (is_add) { + buf = kzalloc(sizeof(struct rkvpss_offline_buf), GFP_KERNEL); + if (!buf) + goto end; + init_vb2(ofl, buf); + mem = ops->attach_dmabuf(&buf->vb, hw->dev, dbuf, dbuf->size); + if (IS_ERR(mem)) { + v4l2_err(&ofl->v4l2_dev, "failed to attach dmabuf, fd:%d\n", fd); + dma_buf_put(dbuf); + kfree(buf); + buf = NULL; + goto end; + } + if (ops->map_dmabuf(mem)) { + v4l2_err(&ofl->v4l2_dev, "failed to map, fd:%d\n", fd); + ops->detach_dmabuf(mem); + dma_buf_put(dbuf); + mem = NULL; + kfree(buf); + buf = NULL; + goto end; + } + buf->dev_id = id; + buf->fd = fd; + buf->file = file; + buf->dbuf = dbuf; + buf->mem = mem; + buf->alloc = false; + list_add_tail(&buf->list, &ofl->list); + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p dev_id:%d fd:%d dbuf:%p size:%d\n", __func__, + file, id, fd, dbuf, size); + } else { + dma_buf_put(dbuf); + } +end: + mutex_unlock(&hw->dev_lock); + return buf; +} + +static int internal_buf_alloc(struct file *file, struct rkvpss_buf_info *info) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *ops = hw->mem_ops; + struct rkvpss_offline_buf *buf; + struct dma_buf *dbuf; + int fd, i, size; + void *mem; + + for (i = 0; i < info->buf_cnt; i++) { + info->buf_fd[i] = -1; + size = PAGE_ALIGN(info->buf_size[i]); + if (!size) + continue; + buf = kzalloc(sizeof(struct rkvpss_offline_buf), GFP_KERNEL); + if (!buf) + goto err; + init_vb2(ofl, buf); + mem = ops->alloc(&buf->vb, hw->dev, size); + if (IS_ERR_OR_NULL(mem)) { + kfree(buf); + goto err; + } + dbuf = ops->get_dmabuf(&buf->vb, mem, O_RDWR); + if (IS_ERR_OR_NULL(dbuf)) { + ops->put(mem); + kfree(buf); + goto err; + } + fd = dma_buf_fd(dbuf, O_CLOEXEC); + if (fd < 0) { + dma_buf_put(dbuf); + ops->put(mem); + kfree(buf); + goto err; + } + get_dma_buf(dbuf); + + info->buf_fd[i] = fd; + buf->fd = fd; + buf->file = file; + buf->dbuf = dbuf; + buf->mem = mem; + buf->alloc = true; + buf->dev_id = info->dev_id; + ops->prepare(buf->mem); + mutex_lock(&hw->dev_lock); + list_add_tail(&buf->list, &ofl->list); + mutex_unlock(&hw->dev_lock); + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p dev_id:%d fd:%d dbuf:%p\n", + __func__, file, buf->dev_id, fd, dbuf); + } + return 0; +err: + for (i -= 1; i >= 0; i--) + buf_del(file, info->dev_id, info->buf_fd[i], false, false); + return -ENOMEM; +} + +static int external_buf_add(struct file *file, struct rkvpss_buf_info *info) +{ + void *mem; + int i; + + for (i = 0; i < info->buf_cnt; i++) { + mem = buf_add(file, info->dev_id, info->buf_fd[i], info->buf_size[i]); + if (!mem) + goto err; + } + return 0; +err: + for (i -= 1; i >= 0; i--) + buf_del(file, info->dev_id, info->buf_fd[i], false, false); + return -ENOMEM; +} + +static int rkvpss_ofl_buf_add(struct file *file, struct rkvpss_buf_info *info) +{ + int ret; + + if (info->buf_alloc) + ret = internal_buf_alloc(file, info); + else + ret = external_buf_add(file, info); + return ret; +} + +static void rkvpss_ofl_buf_del(struct file *file, struct rkvpss_buf_info *info) +{ + int i; + + for (i = 0; i < info->buf_cnt; i++) + buf_del(file, info->dev_id, info->buf_fd[i], false, false); +} + +static void poly_phase_scale(struct rkvpss_frame_cfg *frame_cfg, + struct rkvpss_offline_dev *ofl, + struct rkvpss_output_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + u32 in_w = cfg->crop_width, in_h = cfg->crop_height; + u32 out_w = cfg->scl_width, out_h = cfg->scl_height; + u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac; + u32 i, j, idx, ratio, val, in_div, out_div, factor; + bool dering_en = false, yuv420_in = false, yuv422_to_420 = false; + + if (in_w == out_w && in_h == out_w) { + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, 0); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, 0); + rkvpss_hw_clear_bits(hw, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS); + goto end; + } else { + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS, + RKVPSS_SCL0_CKG_DIS); + } + + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + if (unite) { + if (left) { + if (in_w == out_w) + val = (cfg->crop_width / 2 - 1) | + ((cfg->crop_height - 1) << 16); + else + val = (cfg->crop_width / 2 + UNITE_ENLARGE - 1) | + ((cfg->crop_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); + } else { + val = (ALIGN(ofl->unite_params[0].right_scl_need_size_y + 3, 2) - 1) | + ((cfg->crop_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); + val = (ALIGN(ofl->unite_params[0].right_scl_need_size_c + 6, 2) - 1) | + ((cfg->crop_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); + } + val = (cfg->scl_width / 2 - 1) | ((cfg->scl_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); + } else { + val = (in_w - 1) | ((in_h - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); + val = (out_w - 1) | ((out_h - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); + } + + ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; + if (dering_en) { + ctrl |= RKVPSS_ZME_DERING_EN; + rkvpss_hw_write(hw, RKVPSS_ZME_Y_DERING_PARA, 0xd10410); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_DERING_PARA, 0xd10410); + } + if (in_w != out_w) { + if (in_w > out_w) { + factor = 4096; + ctrl |= RKVPSS_ZME_XSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_XSU_EN; + } + y_xscl_fac = (in_w - 1) * factor / (out_w - 1); + uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1); + + ratio = y_xscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j], + rkvpss_zme_tap8_coe[idx][i][j + 1]); + rkvpss_hw_write(hw, + RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, val); + rkvpss_hw_write(hw, + RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, val); + } + } + } else { + y_xscl_fac = 0; + uv_xscl_fac = 0; + } + if (unite && !left) { + rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac | + (ofl->unite_params[0].y_w_phase << 16)); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac | + (ofl->unite_params[0].c_w_phase << 16)); + } else { + rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); + } + + if (in_h != out_h) { + if (in_h > out_h) { + factor = 4096; + ctrl |= RKVPSS_ZME_YSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_YSU_EN; + } + y_yscl_fac = (in_h - 1) * factor / (out_h - 1); + uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1); + + ratio = y_yscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j], + rkvpss_zme_tap6_coe[idx][i][j + 1]); + rkvpss_hw_write(hw, + RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, val); + rkvpss_hw_write(hw, + RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, val); + } + } + } else { + y_yscl_fac = 0; + uv_yscl_fac = 0; + } + rkvpss_hw_write(hw, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac); + + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, ctrl); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, ctrl); + + if (unite) { + val = cfg->scl_width / 2; + rkvpss_hw_write(hw, RKVPSS_ZME_H_SIZE, (val << 16) | val); + rkvpss_hw_write(hw, RKVPSS_ZME_H_OFFS, 0); + if (!left) { + val = cfg->scl_width / 2 - ALIGN_DOWN(cfg->scl_width / 2, 16); + rkvpss_hw_write(hw, RKVPSS_ZME_H_OFFS, (3 << 20) | (3 << 16) | + (ofl->unite_params[0].scl_in_crop_w_y << 8) | + (ofl->unite_params[0].scl_in_crop_w_c << 12) | + (val << 4) | val); + } + } + + ctrl = RKVPSS_ZME_GATING_EN; + if (yuv420_in) + ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; + if (yuv422_to_420) + ctrl |= RKVPSS_ZME_422TO420_EN; + if (unite) { + if (left) + ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_8K_EN; + else + ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_IN_CLIP_EN | RKVPSS_ZME_8K_EN; + } + rkvpss_hw_write(hw, RKVPSS_ZME_CTRL, ctrl); +end: + val = RKVPSS_ZME_GEN_UPD | RKVPSS_ZME_FORCE_UPD; + rkvpss_hw_write(hw, RKVPSS_ZME_UPDATE, val); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d hw ctrl:0x%x y_src_size:0x%x y_dst_size:0x%x\n", + __func__, unite, left, + rkvpss_hw_read(hw, RKVPSS_ZME_CTRL), + rkvpss_hw_read(hw, RKVPSS_ZME_Y_SRC_SIZE), + rkvpss_hw_read(hw, RKVPSS_ZME_Y_DST_SIZE)); +} + +static void bilinear_scale(struct rkvpss_frame_cfg *frame_cfg, + struct rkvpss_offline_dev *ofl, + struct rkvpss_output_cfg *cfg, int idx, bool unite, bool left) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + u32 in_w = cfg->crop_width, in_h = cfg->crop_height; + u32 out_w = cfg->scl_width, out_h = cfg->scl_height; + u32 reg_base, in_div, out_div, val, ctrl = 0, clk_mask = 0; + bool yuv420_in = false, yuv422_to_420 = false; + + switch (idx) { + case RKVPSS_OUTPUT_CH1: + reg_base = RKVPSS_SCALE1_BASE; + clk_mask = RKVPSS_SCL1_CKG_DIS; + break; + case RKVPSS_OUTPUT_CH2: + reg_base = RKVPSS_SCALE2_BASE; + clk_mask = RKVPSS_SCL2_CKG_DIS; + break; + case RKVPSS_OUTPUT_CH3: + reg_base = RKVPSS_SCALE3_BASE; + clk_mask = RKVPSS_SCL3_CKG_DIS; + break; + default: + return; + } + + /*config scl clk gate*/ + if (in_w == out_w && in_h == out_h) + rkvpss_hw_clear_bits(hw, RKVPSS_VPSS_CLK_GATE, clk_mask); + else + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CLK_GATE, clk_mask, clk_mask); + + if (!unite) { + if (in_w == out_w && in_h == out_w) + goto end; + + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + val = in_w | (in_h << 16); + rkvpss_hw_write(hw, reg_base + 0x8, val); + val = out_w | (out_h << 16); + rkvpss_hw_write(hw, reg_base + 0xc, val); + + if (in_w != out_w) { + val = (in_w - 1) * 4096 / (out_w - 1); + rkvpss_hw_write(hw, reg_base + 0x10, val); + val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); + rkvpss_hw_write(hw, reg_base + 0x14, val); + + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + if (in_h != out_h) { + val = (in_h - 1) * 4096 / (out_h - 1); + rkvpss_hw_write(hw, reg_base + 0x18, val); + val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); + rkvpss_hw_write(hw, reg_base + 0x1c, val); + + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + } else { + if (left) { + rkvpss_hw_write(hw, reg_base + 0x50, 0); + rkvpss_hw_write(hw, reg_base + 0x20, 0); + rkvpss_hw_write(hw, reg_base + 0x24, 0); + rkvpss_hw_write(hw, reg_base + 0x48, 0); + rkvpss_hw_write(hw, reg_base + 0x4c, 0); + if (in_w == out_w) + val = (cfg->crop_width / 2) | (cfg->crop_height << 16); + else + val = (cfg->crop_width / 2 + UNITE_ENLARGE) | + (cfg->crop_height << 16); + rkvpss_hw_write(hw, reg_base + 0x8, val); + val = cfg->scl_width / 2 | (cfg->scl_height << 16); + rkvpss_hw_write(hw, reg_base + 0xc, val); + ctrl |= RKVPSS_SCL_CLIP_EN; + } else { + val = ofl->unite_params[idx].scl_in_crop_w_y | + (ofl->unite_params[idx].scl_in_crop_w_c << 4); + rkvpss_hw_write(hw, reg_base + 0x50, val); + rkvpss_hw_write(hw, reg_base + 0x20, ofl->unite_params[idx].y_w_phase); + rkvpss_hw_write(hw, reg_base + 0x24, ofl->unite_params[idx].c_w_phase); + val = cfg->scl_width / 2 - ALIGN_DOWN(cfg->scl_width / 2, 16); + rkvpss_hw_write(hw, reg_base + 0x48, val); + rkvpss_hw_write(hw, reg_base + 0x4c, val); + val = (cfg->crop_width / 2 + ofl->unite_right_enlarge) | + (cfg->crop_height << 16); + rkvpss_hw_write(hw, reg_base + 0x8, val); + val = cfg->scl_width / 2 | (cfg->scl_height << 16); + rkvpss_hw_write(hw, reg_base + 0xc, val); + ctrl |= RKVPSS_SCL_CLIP_EN | RKVPSS_SCL_IN_CLIP_EN; + } + if (cfg->scl_width != frame_cfg->input.width) { + rkvpss_hw_write(hw, reg_base + 0x10, ofl->unite_params[idx].y_w_fac); + rkvpss_hw_write(hw, reg_base + 0x14, ofl->unite_params[idx].c_w_fac); + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + if (cfg->scl_height != frame_cfg->input.height) { + rkvpss_hw_write(hw, reg_base + 0x18, ofl->unite_params[idx].y_h_fac); + rkvpss_hw_write(hw, reg_base + 0x1c, ofl->unite_params[idx].c_h_fac); + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + } + +end: + rkvpss_hw_write(hw, reg_base, ctrl); + val = RKVPSS_SCL_GEN_UPD | RKVPSS_SCL_FORCE_UPD; + rkvpss_hw_write(hw, reg_base + 0x4, val); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d hw ch:%d ctrl:0x%x y_src_size:0x%x y_dst_size:0x%x\n", + __func__, unite, left, idx, + rkvpss_hw_read(hw, reg_base), + rkvpss_hw_read(hw, reg_base + 0x8), + rkvpss_hw_read(hw, reg_base + 0xc)); +} + +static void scale_config(struct file *file, + struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int i; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + + if (i == RKVPSS_OUTPUT_CH0) + poly_phase_scale(cfg, ofl, &cfg->output[i], unite, left); + else + bilinear_scale(cfg, ofl, &cfg->output[i], i, unite, left); + } +} + +static int cmsc_config(struct rkvpss_offline_dev *ofl, + struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + struct rkvpss_cmsc_cfg *cmsc_cfg, tmp_cfg = {0}; + struct rkvpss_cmsc_win *win; + struct rkvpss_cmsc_point *point; + int i, j, k; + u32 ch_win_en[RKVPSS_OUT_V1_MAX]; + u32 ch_win_mode[RKVPSS_OUT_V1_MAX]; + u32 win_color[RKVPSS_CMSC_WIN_MAX]; + u32 val, slope, hor, mask, mosaic_block = 0, ctrl = 0; + u32 hw_in_w, hw_in_h; + + if (!hw->is_ofl_cmsc) + return 0; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + ch_win_en[i] = 0; + ch_win_mode[i] = 0; + cmsc_cfg = &cfg->output[i].cmsc; + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + if (i == 0) + win_color[j] = 0; + if (!cmsc_cfg->win[j].win_en) + continue; + ch_win_en[i] |= BIT(j); + ch_win_mode[i] |= cmsc_cfg->win[j].mode ? BIT(j) : 0; + /** mosaic_block use the last channel **/ + if (cmsc_cfg->win[j].mode) + mosaic_block = cfg->output[i].cmsc.mosaic_block; + /** window cover all channel consistent **/ + if (!cfg->output[i].cmsc.win[j].mode) { + win_color[j] = RKVPSS_CMSK_WIN_YUV( + cfg->output[i].cmsc.win[j].cover_color_y, + cfg->output[i].cmsc.win[j].cover_color_u, + cfg->output[i].cmsc.win[j].cover_color_v); + if (cfg->output[i].cmsc.win[j].cover_color_a > 15) + cfg->output[i].cmsc.win[j].cover_color_a = 15; + win_color[j] |= RKVPSS_CMSC_WIN_ALPHA( + cfg->output[i].cmsc.win[j].cover_color_a); + } + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) + tmp_cfg.win[j].point[k] = cmsc_cfg->win[j].point[k]; + } + } + + /* deal unite left params */ + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!unite || !left) + break; + if (!cfg->output[i].enable) + continue; + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + win = &tmp_cfg.win[j]; + if (!(ch_win_en[i] & BIT(j))) + continue; + mask = 0; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + point = &win->point[k]; + if (point->x >= cfg->input.width / 2) + mask |= BIT(k); + else + mask &= ~BIT(k); + } + if (mask == 0xf) { + /** all right **/ + ch_win_en[i] &= ~BIT(j); + } else if (mask != 0) { + /** middle need avoid pentagon **/ + if (win->point[0].x != win->point[3].x || + win->point[1].x != win->point[2].x) { + ch_win_en[i] &= ~BIT(j); + } else { + win->point[1].x = cfg->input.width / 2; + win->point[2].x = cfg->input.width / 2; + } + } + } + } + + /* deal unite right params */ + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!unite || left) + break; + if (!cfg->output[i].enable) + continue; + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + win = &tmp_cfg.win[j]; + if (!(ch_win_en[i] & BIT(j))) + continue; + mask = 0; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + point = &win->point[k]; + if (point->x <= cfg->input.width / 2) + mask |= BIT(k); + else + mask &= ~BIT(k); + } + if (mask == 0xf) { + /** all left **/ + ch_win_en[i] &= ~BIT(j); + } else if (mask != 0) { + /** middle need avoid pentagon **/ + if (win->point[0].x != win->point[3].x || + win->point[1].x != win->point[2].x) { + ch_win_en[i] &= ~BIT(j); + } else { + win->point[0].x = ofl->unite_right_enlarge; + win->point[3].x = ofl->unite_right_enlarge; + win->point[1].x = win->point[1].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[2].x = win->point[2].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + } + } else { + /** all right **/ + win->point[0].x = win->point[0].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[1].x = win->point[1].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[2].x = win->point[2].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[3].x = win->point[3].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + } + } + } + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + if (ch_win_en[i]) { + ctrl |= RKVPSS_CMSC_EN; + ctrl |= RKVPSS_CMSC_CHN_EN(i); + } + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_WIN + i * 4, ch_win_en[i]); + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_MODE + i * 4, ch_win_mode[i]); + hw_in_w = rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_WIDTH); + hw_in_h = rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_HEIGHT); + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + if (!(ch_win_en[i] & BIT(j))) + continue; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + if (tmp_cfg.win[j].point[k].x > hw_in_w || + tmp_cfg.win[j].point[k].y > hw_in_h) { + v4l2_err(&ofl->v4l2_dev, + "%s cmsc coordinate error dev_id:%d unite:%u left:%u ch:%d win:%d point:%d x:%u y:%u hw_in_w:%u hw_in_h:%u\n", + __func__, cfg->dev_id, unite, left, + i, j, k, + tmp_cfg.win[j].point[k].x, + tmp_cfg.win[j].point[k].y, + hw_in_w, hw_in_w); + return -EINVAL; + } + val = RKVPSS_CMSC_WIN_VTX(tmp_cfg.win[j].point[k].x, + tmp_cfg.win[j].point[k].y); + rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val); + rkvpss_cmsc_slop(&tmp_cfg.win[j].point[k], + (k + 1 == RKVPSS_CMSC_POINT_MAX) ? + &tmp_cfg.win[j].point[0] : + &tmp_cfg.win[j].point[k + 1], + &slope, &hor); + val = RKVPSS_CMSC_WIN_SLP(slope, hor); + rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val); + v4l2_dbg(4, rkvpss_debug, &ofl->v4l2_dev, + "%s dev_id:%d, unite:%d left:%d ch:%d win:%d point:%d x:%u y:%u", + __func__, cfg->dev_id, unite, left, i, j, k, + tmp_cfg.win[j].point[k].x, + tmp_cfg.win[j].point[k].y); + } + if ((ch_win_mode[i] & BIT(j))) + continue; + rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_PARA + j * 4, win_color[j]); + } + } + + ctrl |= RKVPSS_CMSC_BLK_SZIE(mosaic_block); + rkvpss_hw_write(hw, RKVPSS_CMSC_CTRL, ctrl); + + val = RKVPSS_CMSC_GEN_UPD | RKVPSS_CMSC_FORCE_UPD; + rkvpss_hw_write(hw, RKVPSS_CMSC_UPDATE, val); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s dev_id:%d, unite:%d left:%d hw ctrl:0x%x update_val:0x%x", + __func__, cfg->dev_id, unite, left, ctrl, val); + + return 0; +} + +static void aspt_config(struct file *file, + struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + u32 reg_base, val; + int i; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + + switch (i) { + case RKVPSS_OUTPUT_CH0: + reg_base = RKVPSS_RATIO0_BASE; + break; + case RKVPSS_OUTPUT_CH1: + reg_base = RKVPSS_RATIO1_BASE; + break; + case RKVPSS_OUTPUT_CH2: + reg_base = RKVPSS_RATIO2_BASE; + break; + case RKVPSS_OUTPUT_CH3: + reg_base = RKVPSS_RATIO3_BASE; + break; + default: + return; + } + + if (!cfg->output[i].aspt.enable) { + rkvpss_hw_write(hw, reg_base, 0); + val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; + rkvpss_hw_write(hw, reg_base + 0x4, val); + continue; + } + val = cfg->output[i].scl_width | (cfg->output[i].scl_height << 16); + rkvpss_hw_write(hw, reg_base + 0x10, val); + val = cfg->output[i].aspt.width | (cfg->output[i].aspt.height << 16); + rkvpss_hw_write(hw, reg_base + 0x14, val); + val = cfg->output[i].aspt.h_offs | (cfg->output[i].aspt.v_offs << 16); + rkvpss_hw_write(hw, reg_base + 0x18, val); + val = cfg->output[i].aspt.color_y | + (cfg->output[i].aspt.color_u << 16) | + (cfg->output[i].aspt.color_v << 24); + rkvpss_hw_write(hw, reg_base + 0x1c, val); + rkvpss_hw_write(hw, reg_base, RKVPSS_RATIO_EN); + val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; + rkvpss_hw_write(hw, reg_base + 0x4, val); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s hw ch:%d ctrl:0x%x in_size:0x%x out_size:0x%x offset:0x%x\n", + __func__, i, + rkvpss_hw_read(hw, reg_base), + rkvpss_hw_read(hw, reg_base + 0x10), + rkvpss_hw_read(hw, reg_base + 0x14), + rkvpss_hw_read(hw, reg_base + 0x18)); + } +} + +static void add_cfginfo(struct rkvpss_offline_dev *ofl, struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_ofl_cfginfo *cfginfo = NULL, *new_cfg = NULL, *first_cfg = NULL; + int i, count = 0; + + new_cfg = kzalloc(sizeof(struct rkvpss_ofl_cfginfo), GFP_KERNEL); + new_cfg->dev_id = cfg->dev_id; + new_cfg->sequence = cfg->sequence; + new_cfg->input.buf_fd = cfg->input.buf_fd; + new_cfg->input.format = cfg->input.format; + new_cfg->input.width = cfg->input.width; + new_cfg->input.height = cfg->input.height; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + new_cfg->output[i].enable = cfg->output[i].enable; + new_cfg->output[i].buf_fd = cfg->output[i].buf_fd; + new_cfg->output[i].format = cfg->output[i].format; + new_cfg->output[i].crop_v_offs = cfg->output[i].crop_v_offs; + new_cfg->output[i].crop_h_offs = cfg->output[i].crop_h_offs; + new_cfg->output[i].crop_width = cfg->output[i].crop_width; + new_cfg->output[i].crop_height = cfg->output[i].crop_height; + new_cfg->output[i].scl_width = cfg->output[i].scl_width; + new_cfg->output[i].scl_height = cfg->output[i].scl_height; + } + + mutex_lock(&ofl->ofl_lock); + list_for_each_entry(cfginfo, &ofl->cfginfo_list, list) { + count++; + } + while (count >= rkvpss_cfginfo_num && count != 0) { + first_cfg = list_first_entry(&ofl->cfginfo_list, struct rkvpss_ofl_cfginfo, list); + list_del_init(&first_cfg->list); + kfree(first_cfg); + count--; + } + if (rkvpss_cfginfo_num) + list_add_tail(&new_cfg->list, &ofl->cfginfo_list); + else + kfree(new_cfg); + mutex_unlock(&ofl->ofl_lock); +} + + +static int read_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *mem_ops = hw->mem_ops; + struct sg_table *sg_tbl; + struct rkvpss_offline_buf *buf; + u32 in_ctrl, in_size, in_c_offs, unite_r_offs, val, mask, unite_off = 0, enlarge = 0; + + in_c_offs = 0; + in_ctrl = 0; + switch (cfg->input.format) { + case V4L2_PIX_FMT_NV16: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = cfg->input.stride * cfg->input.height; + in_size = cfg->input.stride * cfg->input.height * 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; + unite_off = 8; + break; + case V4L2_PIX_FMT_NV12: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = cfg->input.stride * cfg->input.height; + in_size = cfg->input.stride * cfg->input.height * 3 / 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; + unite_off = 8; + break; + case V4L2_PIX_FMT_NV61: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = cfg->input.stride * cfg->input.height; + in_size = cfg->input.stride * cfg->input.height * 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP | RKVPSS_MI_RD_UV_SWAP; + unite_off = 8; + break; + case V4L2_PIX_FMT_NV21: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = cfg->input.stride * cfg->input.height; + in_size = cfg->input.stride * cfg->input.height * 3 / 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_420SP | RKVPSS_MI_RD_UV_SWAP; + unite_off = 8; + break; + case V4L2_PIX_FMT_RGB565: + if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 2, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565; + unite_off = 16; + break; + case V4L2_PIX_FMT_RGB565X: + if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 2, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565 | RKVPSS_MI_RD_RB_SWAP; + unite_off = 16; + break; + case V4L2_PIX_FMT_RGB24: + if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 3, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888; + unite_off = 24; + break; + case V4L2_PIX_FMT_BGR24: + if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 3, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888 | RKVPSS_MI_RD_RB_SWAP; + unite_off = 24; + break; + case V4L2_PIX_FMT_XRGB32: + if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 4, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888; + unite_off = 32; + break; + case V4L2_PIX_FMT_XBGR32: + if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 4, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP; + unite_off = 32; + break; + case V4L2_PIX_FMT_RGBX32: + if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 4, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 + | RKVPSS_MI_RD_ALPHA_SWAP; + unite_off = 32; + break; + case V4L2_PIX_FMT_BGRX32: + if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 4, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 + | RKVPSS_MI_RD_RB_SWAP + | RKVPSS_MI_RD_ALPHA_SWAP; + unite_off = 32; + break; + case V4L2_PIX_FMT_FBC0: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = 0; + in_size = cfg->input.stride * cfg->input.height * 3 / 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; + break; + case V4L2_PIX_FMT_FBC2: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = 0; + in_size = cfg->input.stride * cfg->input.height * 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; + break; + case V4L2_PIX_FMT_FBC4: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = 0; + in_size = cfg->input.stride * cfg->input.height * 3; + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP | RKVPSS_MI_RD_FBCD_YUV444_EN; + break; + case V4L2_PIX_FMT_TILE420: + if (cfg->input.stride < ALIGN(cfg->input.width * 6, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 6, 16); + in_c_offs = 0; + in_size = cfg->input.stride * (cfg->input.height / 4); + in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; + break; + case V4L2_PIX_FMT_TILE422: + if (cfg->input.stride < ALIGN(cfg->input.width * 8, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 8, 16); + in_c_offs = 0; + in_size = cfg->input.stride * (cfg->input.height / 4); + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support input format:%c%c%c%c\n", + cfg->dev_id, cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + return -EINVAL; + } + + buf = buf_add(file, cfg->dev_id, cfg->input.buf_fd, in_size); + if (!buf) + return -ENOMEM; + + sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); + + if (!unite) { + val = cfg->input.width; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); + val = cfg->input.height; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); + val = sg_dma_address(sg_tbl->sgl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); + val += in_c_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); + } else { + val = cfg->input.height; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); + ofl->unite_right_enlarge = ALIGN(cfg->input.width / 2, 16) - + (cfg->input.width / 2) + 16; + + if (left) { + if (!cfg->mirror) + enlarge = UNITE_LEFT_ENLARGE; + else + enlarge = ofl->unite_right_enlarge; + val = cfg->input.width / 2 + enlarge; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); + val = sg_dma_address(sg_tbl->sgl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); + val += in_c_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); + } else { + if (!cfg->mirror) + enlarge = ofl->unite_right_enlarge; + else + enlarge = UNITE_LEFT_ENLARGE; + val = cfg->input.width / 2 + enlarge; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); + val = (cfg->input.width / 2 - enlarge) * unite_off; + unite_r_offs = ALIGN_DOWN(val / 8, 16); + val = sg_dma_address(sg_tbl->sgl) + unite_r_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); + val += in_c_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); + } + } + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d hw width:%d height:%d y_base:0x%x\n", + __func__, unite, left, + rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_WIDTH), + rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_HEIGHT), + rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_BASE)); + + if (cfg->input.format == V4L2_PIX_FMT_FBC0 || + cfg->input.format == V4L2_PIX_FMT_FBC2 || + cfg->input.format == V4L2_PIX_FMT_FBC4) { + in_ctrl |= RKVPSS_MI_RD_MODE(2) | RKVPSS_MI_RD_FBCD_OPT_DIS; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, 0); + } else { + if (cfg->input.format == V4L2_PIX_FMT_TILE420 || + cfg->input.format == V4L2_PIX_FMT_TILE422) { + in_ctrl |= RKVPSS_MI_RD_MODE(1); + switch (cfg->input.rotate) { + case ROTATE_90: + in_ctrl |= RKVPSS_MI_RD_ROT_90; + break; + case ROTATE_180: + in_ctrl |= RKVPSS_MI_RD_ROT_180; + break; + case ROTATE_270: + in_ctrl |= RKVPSS_MI_RD_ROT_270; + break; + default: + in_ctrl |= RKVPSS_MI_RD_ROT_0; + break; + } + } + val = cfg->input.stride; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, val); + } + + mask = RKVPSS_MI_RD_GROUP_MODE(3) | RKVPSS_MI_RD_BURST16_LEN; + rkvpss_hw_set_bits(hw, RKVPSS_MI_RD_CTRL, ~mask, in_ctrl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_INIT, RKVPSS_MI_RD_FORCE_UPD); + + return 0; +} + +static void crop_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + int i; + u32 reg, val, crop_en; + + crop_en = 0; + if (!unite) { + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + reg = RKVPSS_CROP0_0_H_OFFS; + val = cfg->output[i].crop_h_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_OFFS; + val = cfg->output[i].crop_v_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_H_SIZE; + val = cfg->output[i].crop_width; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_SIZE; + val = cfg->output[i].crop_height; + rkvpss_hw_write(hw, reg + i * 0x10, val); + crop_en |= RKVPSS_CROP_CHN_EN(i); + } + } else { + if (left) { + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + + reg = RKVPSS_CROP0_0_H_OFFS; + val = cfg->output[i].crop_h_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_OFFS; + val = cfg->output[i].crop_v_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_H_SIZE; + /*if no scale, left don't enlarge*/ + if (cfg->output[i].crop_width == cfg->output[i].scl_width) + val = cfg->output[i].crop_width / 2; + else + val = cfg->output[i].crop_width / 2 + UNITE_LEFT_ENLARGE; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_SIZE; + val = cfg->output[i].crop_height; + rkvpss_hw_write(hw, reg + i * 0x10, val); + crop_en |= RKVPSS_CROP_CHN_EN(i); + } + } else { + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + reg = RKVPSS_CROP0_0_H_OFFS; + val = ofl->unite_params[i].quad_crop_w; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_OFFS; + val = cfg->output[i].crop_v_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_H_SIZE; + val = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + ofl->unite_params[i].quad_crop_w; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_SIZE; + val = cfg->output[i].crop_height; + rkvpss_hw_write(hw, reg + i * 0x10, val); + crop_en |= RKVPSS_CROP_CHN_EN(i); + } + } + } + rkvpss_hw_write(hw, RKVPSS_CROP0_CTRL, crop_en); + rkvpss_hw_write(hw, RKVPSS_CROP0_UPDATE, RKVPSS_CROP_FORCE_UPD); + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d hw ch:%d h_offs:%d v_offs:%d width:%d height:%d\n", + __func__, unite, left, i, + rkvpss_hw_read(hw, RKVPSS_CROP0_0_H_OFFS + i * 0x10), + rkvpss_hw_read(hw, RKVPSS_CROP0_0_V_OFFS + i * 0x10), + rkvpss_hw_read(hw, RKVPSS_CROP0_0_H_SIZE + i * 0x10), + rkvpss_hw_read(hw, RKVPSS_CROP0_0_V_SIZE + i * 0x10)); + } +} + +static int write_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *mem_ops = hw->mem_ops; + struct sg_table *sg_tbl; + struct rkvpss_offline_buf *buf; + struct rkvpss_output_ch out_ch[RKVPSS_OUT_V1_MAX] = { 0 }; + int i; + u32 w, h, val, reg, mask, mi_update, flip_en, unite_off = 0; + bool ch_en = false, wr_uv_swap = false; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!hw->is_ofl_ch[i] && cfg->output[i].enable) { + v4l2_err(&ofl->v4l2_dev, + "dev_id:%d ch%d no select for offline mode, set to disable\n", + cfg->dev_id, i); + cfg->output[i].enable = 0; + } + + if (!cfg->output[i].enable) + continue; + ch_en = true; + + if (cfg->output[i].aspt.enable) { + w = cfg->output[i].aspt.width; + h = cfg->output[i].aspt.height; + } else { + w = cfg->output[i].scl_width; + h = cfg->output[i].scl_height; + } + if (i == RKVPSS_OUTPUT_CH1) { + bool is_fmt_find = true; + + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_RGB565: + if (cfg->output[i].stride < ALIGN(w * 2, 16)) + cfg->output[i].stride = ALIGN(w * 2, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB565 | + RKVPSS_MI_CHN_WR_RB_SWAP; + break; + case V4L2_PIX_FMT_RGB24: + if (cfg->output[i].stride < ALIGN(w * 3, 16)) + cfg->output[i].stride = ALIGN(w * 3, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB888 | + RKVPSS_MI_CHN_WR_RB_SWAP; + break; + case V4L2_PIX_FMT_RGB565X: + if (cfg->output[i].stride < ALIGN(w * 2, 16)) + cfg->output[i].stride = ALIGN(w * 2, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB565; + break; + case V4L2_PIX_FMT_BGR24: + if (cfg->output[i].stride < ALIGN(w * 3, 16)) + cfg->output[i].stride = ALIGN(w * 3, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB888; + break; + case V4L2_PIX_FMT_XBGR32: + if (cfg->output[i].stride < ALIGN(w * 4, 16)) + cfg->output[i].stride = ALIGN(w * 4, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_ARGB888; + break; + case V4L2_PIX_FMT_XRGB32: + if (cfg->output[i].stride < ALIGN(w * 4, 16)) + cfg->output[i].stride = ALIGN(w * 4, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_ARGB888 + | RKVPSS_MI_CHN_WR_RB_SWAP; + break; + default: + is_fmt_find = false; + } + if (is_fmt_find) { + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; + out_ch[i].size = cfg->output[i].stride * h; + continue; + } + } + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_UYVY: + if (cfg->output[i].stride < ALIGN(w * 2, 16)) + cfg->output[i].stride = ALIGN(w * 2, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_422P | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_NV16: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h * 2; + out_ch[i].c_offs = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_NV12: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV420; + out_ch[i].size = cfg->output[i].stride * h * 3 / 2; + out_ch[i].c_offs = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_GREY: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV400; + out_ch[i].size = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_VYUY: + if (cfg->output[i].stride < ALIGN(w * 2, 16)) + cfg->output[i].stride = ALIGN(w * 2, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_422P | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_NV61: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h * 2; + out_ch[i].c_offs = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_NV21: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV420; + out_ch[i].size = cfg->output[i].stride * h * 3 / 2; + out_ch[i].c_offs = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_TILE420: + if (cfg->output[i].stride < ALIGN(w * 6, 16)) + cfg->output[i].stride = ALIGN(w * 6, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_YUV420; + out_ch[i].size = cfg->output[i].stride * (h / 4); + out_ch[i].c_offs = 0; + break; + case V4L2_PIX_FMT_TILE422: + if (cfg->output[i].stride < ALIGN(w * 8, 16)) + cfg->output[i].stride = ALIGN(w * 8, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * (h / 4); + out_ch[i].c_offs = 0; + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support output ch%d format:%c%c%c%c\n", + cfg->dev_id, i, + cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + return -EINVAL; + } + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; + } + if (!ch_en) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no output channel enable\n", cfg->dev_id); + return -EINVAL; + } + + mi_update = 0; + flip_en = 0; + mask = 0; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (hw->is_ofl_ch[i]) + mask |= RKVPSS_MI_CHN_V_FLIP(i); + if (!cfg->output[i].enable) + continue; + buf = buf_add(file, cfg->dev_id, cfg->output[i].buf_fd, out_ch[i].size); + if (!buf) + goto free_buf; + + if (unite && !left) + unite_off = (ALIGN_DOWN(cfg->output[i].scl_width / 2, 16) * 8) / 8; + + if (cfg->output[i].aspt.enable) + h = cfg->output[i].aspt.height; + else + h = cfg->output[i].scl_height; + + sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); + val = sg_dma_address(sg_tbl->sgl) + unite_off; + reg = RKVPSS_MI_CHN0_WR_Y_BASE; + rkvpss_hw_write(hw, reg + i * 0x100, val); + reg = RKVPSS_MI_CHN0_WR_CB_BASE; + val += out_ch[i].c_offs; + rkvpss_hw_write(hw, reg + i * 0x100, val); + + reg = RKVPSS_MI_CHN0_WR_Y_STRIDE; + val = cfg->output[i].stride; + rkvpss_hw_write(hw, reg + i * 0x100, val); + reg = RKVPSS_MI_CHN0_WR_Y_SIZE; + + if (cfg->output[i].format == V4L2_PIX_FMT_TILE420 || + cfg->output[i].format == V4L2_PIX_FMT_TILE422) + val = cfg->output[i].stride * (ALIGN(h, 4) / 4); + else + val = cfg->output[i].stride * h; + rkvpss_hw_write(hw, reg + i * 0x100, val); + reg = RKVPSS_MI_CHN0_WR_CB_SIZE; + val = out_ch[i].size - val; + rkvpss_hw_write(hw, reg + i * 0x100, val); + + reg = RKVPSS_MI_CHN0_WR_CTRL; + val = out_ch[i].ctrl; + rkvpss_hw_write(hw, reg + i * 0x100, val); + + if (cfg->output[i].flip && + !(cfg->output[i].format == V4L2_PIX_FMT_TILE420) && + !(cfg->output[i].format == V4L2_PIX_FMT_TILE422)) { + flip_en |= RKVPSS_MI_CHN_V_FLIP(i); + + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + val = cfg->output[i].stride / 2; + break; + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_XRGB32: + val = cfg->output[i].stride / 4; + break; + default: + val = cfg->output[i].stride; + break; + } + val = val * h; + reg = RKVPSS_MI_CHN0_WR_Y_PIC_SIZE; + rkvpss_hw_write(hw, reg + i * 0x100, val); + } + mi_update |= (RKVPSS_MI_CHN0_FORCE_UPD << i); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d hw ch:%d y_size:%d y_stride:%d y_pic_size:%d y_base:0x%x", + __func__, unite, left, i, + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_SIZE + i * 100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_STRIDE + i * 100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_PIC_SIZE + i * 100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_BASE + i * 100)); + } + + + rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_VFLIP_CTRL, mask, flip_en); + + /* config output uv swap */ + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (cfg->output[i].enable && + (cfg->output[i].format == V4L2_PIX_FMT_VYUY || + cfg->output[i].format == V4L2_PIX_FMT_NV21 || + cfg->output[i].format == V4L2_PIX_FMT_NV61)) + wr_uv_swap = true; + } + if (wr_uv_swap) { + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (cfg->output[i].enable && (cfg->output[i].format == V4L2_PIX_FMT_UYVY || + cfg->output[i].format == V4L2_PIX_FMT_NV12 || + cfg->output[i].format == V4L2_PIX_FMT_NV16)) { + v4l2_err(&ofl->v4l2_dev, + "dev_id:%d wr_uv_swap need to be consistent\n", + cfg->dev_id); + return -EAGAIN; + } + } + } + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (cfg->output[i].format == V4L2_PIX_FMT_VYUY || + cfg->output[i].format == V4L2_PIX_FMT_NV21 || + cfg->output[i].format == V4L2_PIX_FMT_NV61) { + mask = RKVPSS_MI_WR_UV_SWAP; + val = RKVPSS_MI_WR_UV_SWAP; + rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_CTRL, mask, val); + break; + } + } + + for (i = 0; i <= RKVPSS_OUTPUT_CH1; i++) { + if (cfg->output[i].format == V4L2_PIX_FMT_TILE420 || + cfg->output[i].format == V4L2_PIX_FMT_TILE422) { + mask = RKVPSS_MI_WR_TILE_SEL(3); + val = RKVPSS_MI_WR_TILE_SEL(i + 1); + rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_CTRL, mask, val); + } + } + + /* need update two for online2 mode */ + rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); + rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); + + return 0; + +free_buf: + for (i -= 1; i >= 0; i--) { + if (!cfg->output[i].enable) + continue; + buf_del(file, cfg->dev_id, cfg->output[i].buf_fd, false, true); + } + buf_del(file, cfg->dev_id, cfg->input.buf_fd, false, true); + return -ENOMEM; +} + +static void calc_unite_scl_params(struct file *file, struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_unite_scl_params *params; + int i; + u32 right_scl_need_size_y, right_scl_need_size_c; + u32 left_in_used_size_y, left_in_used_size_c; + u32 right_fst_position_y, right_fst_position_c; + u32 right_y_crop_total; + u32 right_c_crop_total; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (cfg->output[i].enable == 0) + continue; + params = &ofl->unite_params[i]; + params->y_w_fac = (cfg->output[i].crop_width - 1) * 4096 / + (cfg->output[i].scl_width - 1); + params->c_w_fac = (cfg->output[i].crop_width / 2 - 1) * 4096 / + (cfg->output[i].scl_width / 2 - 1); + params->y_h_fac = (cfg->output[i].crop_height - 1) * 4096 / + (cfg->output[i].scl_height - 1); + params->c_h_fac = (cfg->output[i].crop_height - 1) * 4096 / + (cfg->output[i].scl_height - 1); + + right_fst_position_y = cfg->output[i].scl_width / 2 * + params->y_w_fac; + right_fst_position_c = cfg->output[i].scl_width / 2 / 2 * + params->c_w_fac; + + left_in_used_size_y = right_fst_position_y >> 12; + left_in_used_size_c = (right_fst_position_c >> 12) * 2; + + params->y_w_phase = right_fst_position_y & 0xfff; + params->c_w_phase = right_fst_position_c & 0xfff; + + right_scl_need_size_y = cfg->output[i].crop_width - + left_in_used_size_y; + params->right_scl_need_size_y = right_scl_need_size_y; + right_scl_need_size_c = cfg->output[i].crop_width - + left_in_used_size_c; + params->right_scl_need_size_c = right_scl_need_size_c; + + if (i == 0 && cfg->output[i].crop_width != cfg->output[i].scl_width) { + right_y_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_y - 3; + right_c_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_c - 6; + } else { + right_y_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_y; + right_c_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_c; + } + + params->quad_crop_w = ALIGN_DOWN(min(right_y_crop_total, right_c_crop_total), 2); + + params->scl_in_crop_w_y = right_y_crop_total - params->quad_crop_w; + params->scl_in_crop_w_c = right_c_crop_total - params->quad_crop_w; + + if (rkvpss_debug >= 4) { + v4l2_info(&ofl->v4l2_dev, + "%s dev_id:%d seq:%d ch:%d y_w_fac:%u c_w_fac:%u y_h_fac:%u c_h_fac:%u\n", + __func__, cfg->dev_id, cfg->sequence, i, params->y_w_fac, + params->c_w_fac, params->y_h_fac, params->c_h_fac); + v4l2_info(&ofl->v4l2_dev, + "\t\t\t\t\t\t unite_right_enlarge:%u", + ofl->unite_right_enlarge); + v4l2_info(&ofl->v4l2_dev, + "\t\t\t\t\t\t y_w_phase:%u c_w_phase:%u quad_crop_w:%u scl_in_crop_w_y:%u scl_in_crop_w_c:%u\n", + params->y_w_phase, params->c_w_phase, + params->quad_crop_w, + params->scl_in_crop_w_y, params->scl_in_crop_w_c); + v4l2_info(&ofl->v4l2_dev, + "\t\t\t\t\t\t right_scl_need_size_y:%u right_scl_need_size_c:%u\n", + params->right_scl_need_size_y, + params->right_scl_need_size_c); + } + } +} + +static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + u32 update, val, mask; + int ret, i; + bool left_tmp; + + if (!unite || left) + add_cfginfo(ofl, cfg); + + init_completion(&ofl->cmpl); + ofl->mode_sel_en = false; + + ret = read_config(file, cfg, unite, left); + if (ret < 0) + return ret; + + if (unite && left) + calc_unite_scl_params(file, cfg); + + if (unite && cfg->mirror) + left_tmp = !left; + else + left_tmp = left; + + ret = cmsc_config(ofl, cfg, unite, left_tmp); + if (ret) + return ret; + crop_config(file, cfg, unite, left_tmp); + scale_config(file, cfg, unite, left_tmp); + if (!unite) + aspt_config(file, cfg); + ret = write_config(file, cfg, unite, left_tmp); + if (ret < 0) + return ret; + + mask = 0; + val = 0; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!hw->is_ofl_ch[i]) + continue; + mask |= (RKVPSS_ISP2VPSS_CHN0_SEL(3) << i * 2); + if (hw->is_ofl_cmsc) + mask |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN; + if (cfg->output[i].enable) + val |= (RKVPSS_ISP2VPSS_CHN0_SEL(1) << i * 2); + } + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_ONLINE, mask, val); + + update = 0; + mask = hw->is_ofl_cmsc ? RKVPSS_MIR_EN : 0; + val = (mask && cfg->mirror) ? RKVPSS_MIR_EN : 0; + if (mask) { + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CTRL, mask, val); + update |= RKVPSS_MIR_FORCE_UPD; + } + update |= RKVPSS_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD; + rkvpss_hw_write(hw, RKVPSS_VPSS_UPDATE, update); + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_IMSC, 0, RKVPSS_ALL_FRM_END); + + if (rkvpss_debug == 6) + rkvpss_dump_reg(ofl, cfg->sequence, 0x3f24); + + rkvpss_hw_write(hw, RKVPSS_MI_RD_START, RKVPSS_MI_RD_ST); + + ret = wait_for_completion_timeout(&ofl->cmpl, msecs_to_jiffies(500)); + if (!ret) { + v4l2_err(&ofl->v4l2_dev, "working timeout\n"); + ret = -EAGAIN; + } else { + ret = 0; + } + + return ret; +} + +static int rkvpss_module_get(struct file *file, + struct rkvpss_module_sel *get) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + int i, ret = 0; + + mutex_lock(&hw->dev_lock); + if (hw->is_ofl_cmsc) + get->mirror_cmsc_en = 1; + else + get->mirror_cmsc_en = 0; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (hw->is_ofl_ch[i]) + get->ch_en[i] = 1; + else + get->ch_en[i] = 0; + } + mutex_unlock(&hw->dev_lock); + + return ret; +} + +static int rkvpss_module_sel(struct file *file, + struct rkvpss_module_sel *sel) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + struct rkvpss_device *vpss; + int i, ret = 0; + + mutex_lock(&hw->dev_lock); + + if (!ofl->mode_sel_en) { + v4l2_err(&ofl->v4l2_dev, "already set module_sel\n"); + ret = -EINVAL; + goto unlock; + } + + for (i = 0; i < hw->dev_num; i++) { + vpss = hw->vpss[i]; + if (vpss && (vpss->vpss_sdev.state & VPSS_START)) { + v4l2_err(&ofl->v4l2_dev, "no support set mode when vpss working\n"); + ret = -EINVAL; + goto unlock; + } + } + + hw->is_ofl_cmsc = !!sel->mirror_cmsc_en; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) + hw->is_ofl_ch[i] = !!sel->ch_en[i]; +unlock: + mutex_unlock(&hw->dev_lock); + return ret; +} + +static int rkvpss_check_params(struct file *file, struct rkvpss_frame_cfg *cfg, bool *unite) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int i, ret = 0, tile_num = 0; + + /* check dev id out of range */ + if (cfg->dev_id >= DEV_NUM_MAX) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d is out of range. range[0, %d]\n", + cfg->dev_id, DEV_NUM_MAX); + ret = -EINVAL; + goto end; + } + + /* set unite mode */ + if (cfg->input.width > RKVPSS_MAX_WIDTH) + *unite = true; + else + *unite = false; + + /* check input format */ + switch (cfg->input.format) { + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_XRGB32: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_RGBX32: + case V4L2_PIX_FMT_BGRX32: + case V4L2_PIX_FMT_FBC0: + case V4L2_PIX_FMT_FBC2: + case V4L2_PIX_FMT_FBC4: + case V4L2_PIX_FMT_TILE420: + case V4L2_PIX_FMT_TILE422: + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support input format:%c%c%c%c\n", + cfg->dev_id, cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + ret = -EINVAL; + goto end; + } + + /* check input size */ + if (cfg->input.width > RKVPSS_UNITE_MAX_WIDTH || + cfg->input.height > RKVPSS_UNITE_MAX_HEIGHT || + cfg->input.width < RKVPSS_MIN_WIDTH || + cfg->input.height < RKVPSS_MIN_HEIGHT) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d input size not support width:%d height:%d\n", + cfg->dev_id, cfg->input.width, cfg->input.height); + ret = -EINVAL; + goto end; + } + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + /* check output format */ + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV21: + break; + case V4L2_PIX_FMT_TILE420: + case V4L2_PIX_FMT_TILE422: + if (i == RKVPSS_OUTPUT_CH0 || i == RKVPSS_OUTPUT_CH1) { + tile_num++; + if (tile_num > 1) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d only ch0 or ch1 can tile write\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].flip) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d tile write no support flip\n", + cfg->dev_id, i); + ret = -EINVAL; + goto end; + } + } else { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", + cfg->dev_id, + i, + cfg->output[i].format, + cfg->output[i].format >> 8, + cfg->output[i].format >> 16, + cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_XRGB32: + if (i != RKVPSS_OUTPUT_CH1) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", + cfg->dev_id, + i, + cfg->output[i].format, + cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", + cfg->dev_id, + i, cfg->output[i].format, + cfg->output[i].format >> 8, + cfg->output[i].format >> 16, + cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + + /* check output size */ + if (cfg->output[i].scl_width > RKVPSS_UNITE_MAX_WIDTH || + cfg->output[i].scl_height > RKVPSS_UNITE_MAX_HEIGHT || + cfg->output[i].scl_width < RKVPSS_MIN_WIDTH || + cfg->output[i].scl_height < RKVPSS_MIN_HEIGHT) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d output size not support width:%d height:%d\n", + cfg->dev_id, + cfg->output[i].scl_width, + cfg->output[i].scl_height); + ret = -EINVAL; + goto end; + } + + /* check crop */ + cfg->output[i].crop_h_offs = ALIGN(cfg->output[i].crop_h_offs, 2); + cfg->output[i].crop_v_offs = ALIGN(cfg->output[i].crop_v_offs, 2); + cfg->output[i].crop_width = ALIGN(cfg->output[i].crop_width, 2); + cfg->output[i].crop_height = ALIGN(cfg->output[i].crop_height, 2); + if (!cfg->input.rotate || cfg->input.rotate == 2) { + if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > + cfg->input.width) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d w:%d) input width:%d\n", + i, cfg->dev_id, cfg->output[i].crop_h_offs, + cfg->output[i].crop_width, cfg->input.width); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > + cfg->input.height) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d h:%d) input height:%d\n", + cfg->dev_id, i, cfg->output[i].crop_v_offs, + cfg->output[i].crop_height, cfg->input.height); + ret = -EINVAL; + goto end; + } + } else { + if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > + cfg->input.height) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d w:%d) input height:%d\n", + i, cfg->dev_id, cfg->output[i].crop_h_offs, + cfg->output[i].crop_width, cfg->input.height); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > + cfg->input.width) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d h:%d) input width:%d\n", + cfg->dev_id, i, cfg->output[i].crop_v_offs, + cfg->output[i].crop_height, cfg->input.width); + ret = -EINVAL; + goto end; + } + } + if (*unite) { + if (cfg->output[i].crop_h_offs != (cfg->input.width - + (cfg->output[i].crop_h_offs + + cfg->output[i].crop_width))) { + v4l2_err(&ofl->v4l2_dev, " dev_id:%d ch%d unite crop_v need centered crop(h_offs:%d w:%d) input width:%d\n", + cfg->dev_id, i, cfg->output[i].crop_h_offs, + cfg->output[i].crop_width, cfg->input.width); + ret = -EINVAL; + goto end; + } + } + + /* check scale */ + if (i == RKVPSS_OUTPUT_CH2 || i == RKVPSS_OUTPUT_CH3) { + if (cfg->output[i].crop_width != cfg->output[i].scl_width && + cfg->output[i].crop_height != cfg->output[i].scl_height) { + if ((!*unite && cfg->output[i].scl_width > 1920) || + (*unite && cfg->output[i].scl_width > 1920 * 2)) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d single scale max width 1920\n", + cfg->dev_id, i); + ret = -EINVAL; + goto end; + } + } + } + } + + /* check rotate */ + switch (cfg->input.rotate) { + case ROTATE_90: + case ROTATE_180: + case ROTATE_270: + if (cfg->input.format != V4L2_PIX_FMT_TILE420 && + cfg->input.format != V4L2_PIX_FMT_TILE422) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d input format:%c%c%c%c not support rotate\n", + cfg->dev_id, cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + ret = -EINVAL; + goto end; + } + break; + default: + break; + } + + /** unite constraints **/ + if (*unite) { + if (cfg->input.format == V4L2_PIX_FMT_FBC0 || + cfg->input.format == V4L2_PIX_FMT_FBC2 || + cfg->input.format == V4L2_PIX_FMT_FBC4 || + cfg->input.format == V4L2_PIX_FMT_TILE420 || + cfg->input.format == V4L2_PIX_FMT_TILE422) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support input this format:%c%c%c%c\n", + cfg->dev_id, + cfg->input.format, + cfg->input.format >> 8, + cfg->input.format >> 16, + cfg->input.format >> 24); + ret = -EINVAL; + goto end; + } + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + if (cfg->output[i].format != V4L2_PIX_FMT_NV12 && + cfg->output[i].format != V4L2_PIX_FMT_NV16 && + cfg->output[i].format != V4L2_PIX_FMT_NV21 && + cfg->output[i].format != V4L2_PIX_FMT_NV61) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support output this format:%c%c%c%c\n", + cfg->dev_id, + cfg->output[i].format, + cfg->output[i].format >> 8, + cfg->output[i].format >> 16, + cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].scl_width > cfg->input.width) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite horizontal no support scale up\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].aspt.enable) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support aspt\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + } + } + +end: + return ret; +} + +static int rkvpss_prepare_run(struct file *file, struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int ret = 0; + bool unite; + int i, j, k; + s64 us = 0; + u64 ns; + ktime_t t = 0; + + ns = ktime_get_ns(); + ofl->dev_rate[cfg->dev_id].in_rate = ns - ofl->dev_rate[cfg->dev_id].in_timestamp; + ofl->dev_rate[cfg->dev_id].in_timestamp = ns; + + ret = rkvpss_check_params(file, cfg, &unite); + if (ret < 0) + goto end; + + /******** show cfg info ********/ + if (rkvpss_debug >= 2) { + t = ktime_get(); + + v4l2_info(&ofl->v4l2_dev, + "%s dev_id:%d seq:%d mirror:%d input:%dx%d buffd:%d format:%c%c%c%c stride:%d rotate:%d\n", + __func__, cfg->dev_id, cfg->sequence, cfg->mirror, + cfg->input.width, cfg->input.height, cfg->input.buf_fd, + cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24, + cfg->input.stride, cfg->input.rotate); + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + v4l2_info(&ofl->v4l2_dev, + "\t\t\tch%d enable:%d crop:(%d,%d)/%dx%d scl:%dx%d flip:%d buffd:%d format:%c%c%c%c stride:%d\n", + i, cfg->output[i].enable, + cfg->output[i].crop_h_offs, cfg->output[i].crop_v_offs, + cfg->output[i].crop_width, cfg->output[i].crop_height, + cfg->output[i].scl_width, cfg->output[i].scl_height, + cfg->output[i].flip, cfg->output[i].buf_fd, + cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24, + cfg->output[i].stride); + if (rkvpss_debug < 4) + break; + if (!cfg->output[i].enable) + continue; + v4l2_info(&ofl->v4l2_dev, + "\t\t\tcmsc mosaic_block:%d width_ro:%d height_ro:%d\n", + cfg->output[i].cmsc.mosaic_block, + cfg->output[i].cmsc.width_ro, + cfg->output[i].cmsc.height_ro); + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + if (!cfg->output[i].cmsc.win[j].win_en) + continue; + v4l2_info(&ofl->v4l2_dev, + "\t\t\t\twin:%d win_en:%u mode:%u color_y:%u color_u:%u color_v:%u color_a:%u\n", + j, + cfg->output[i].cmsc.win[j].win_en, + cfg->output[i].cmsc.win[j].mode, + cfg->output[i].cmsc.win[j].cover_color_y, + cfg->output[i].cmsc.win[j].cover_color_u, + cfg->output[i].cmsc.win[j].cover_color_v, + cfg->output[i].cmsc.win[j].cover_color_a); + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + v4l2_info(&ofl->v4l2_dev, + "\t\t\t\t\tpoint:%d x:%d y:%d\n", + k, + cfg->output[i].cmsc.win[j].point[k].x, + cfg->output[i].cmsc.win[j].point[k].y); + } + } + v4l2_info(&ofl->v4l2_dev, + "\t\t\taspt_en:%d w:%d h:%d h_offs:%d v_offs:%d color_y:%d color_u:%d color_v:%d\n", + cfg->output[i].aspt.enable, + cfg->output[i].aspt.width, + cfg->output[i].aspt.height, + cfg->output[i].aspt.h_offs, + cfg->output[i].aspt.v_offs, + cfg->output[i].aspt.color_y, + cfg->output[i].aspt.color_u, + cfg->output[i].aspt.color_v); + } + } + + if (!unite) { + ret = rkvpss_ofl_run(file, cfg, false, false); + if (ret < 0) + goto end; + } else { + ret = rkvpss_ofl_run(file, cfg, true, true); + if (ret < 0) { + v4l2_err(&ofl->v4l2_dev, "unite left error\n"); + goto end; + } + ret = rkvpss_ofl_run(file, cfg, true, false); + if (ret < 0) { + v4l2_err(&ofl->v4l2_dev, "unite right error\n"); + goto end; + } + } + + if (rkvpss_debug >= 2) { + us = ktime_us_delta(ktime_get(), t); + v4l2_info(&ofl->v4l2_dev, + "%s end, time:%lldus\n", __func__, us); + } + + ns = ktime_get_ns(); + ofl->dev_rate[cfg->dev_id].out_rate = ns - ofl->dev_rate[cfg->dev_id].out_timestamp; + ofl->dev_rate[cfg->dev_id].out_timestamp = ns; + ofl->dev_rate[cfg->dev_id].sequence = cfg->sequence; + ofl->dev_rate[cfg->dev_id].delay = ofl->dev_rate[cfg->dev_id].out_timestamp - + ofl->dev_rate[cfg->dev_id].in_timestamp; + +end: + return ret; +} + +static long rkvpss_ofl_ioctl(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + long ret = 0; + bool unite; + + ofl->pm_need_wait = true; + + if (!arg) { + ret = -EINVAL; + goto out; + } + + switch (cmd) { + case RKVPSS_CMD_MODULE_SEL: + ret = rkvpss_module_sel(file, arg); + break; + case RKVPSS_CMD_MODULE_GET: + ret = rkvpss_module_get(file, arg); + break; + case RKVPSS_CMD_FRAME_HANDLE: + ret = rkvpss_prepare_run(file, arg); + break; + case RKVPSS_CMD_BUF_ADD: + ret = rkvpss_ofl_buf_add(file, arg); + break; + case RKVPSS_CMD_BUF_DEL: + rkvpss_ofl_buf_del(file, arg); + break; + case RKVPSS_CMD_CHECKPARAMS: + ret = rkvpss_check_params(file, arg, &unite); + break; + default: + ret = -EFAULT; + } + +out: + /* notify hw suspend */ + if (ofl->hw->is_suspend) + complete(&ofl->pm_cmpl); + + ofl->pm_need_wait = false; + return ret; +} + +static const struct v4l2_ioctl_ops offline_ioctl_ops = { + .vidioc_default = rkvpss_ofl_ioctl, +}; + +static int ofl_open(struct file *file) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int ret; + + ret = v4l2_fh_open(file); + if (ret) + goto end; + + mutex_lock(&ofl->hw->dev_lock); + ret = pm_runtime_get_sync(ofl->hw->dev); + mutex_unlock(&ofl->hw->dev_lock); + if (ret < 0) + v4l2_fh_release(file); +end: + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p ret:%d\n", __func__, file, ret); + return (ret > 0) ? 0 : ret; +} + +static int ofl_release(struct file *file) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p\n", __func__, file); + + v4l2_fh_release(file); + buf_del(file, 0, 0, true, false); + mutex_lock(&ofl->hw->dev_lock); + pm_runtime_put_sync(ofl->hw->dev); + mutex_unlock(&ofl->hw->dev_lock); + return 0; +} + +static const struct v4l2_file_operations offline_fops = { + .owner = THIS_MODULE, + .open = ofl_open, + .release = ofl_release, + .unlocked_ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = video_ioctl2, +#endif +}; + +static const struct video_device offline_videodev = { + .name = "rkvpss-offline", + .vfl_dir = VFL_DIR_RX, + .fops = &offline_fops, + .ioctl_ops = &offline_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, +}; + +void rkvpss_offline_irq_v1(struct rkvpss_hw_dev *hw, u32 irq) +{ + struct rkvpss_offline_dev *ofl = &hw->ofl_dev; + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s 0x%x\n", __func__, irq); + + if (!completion_done(&ofl->cmpl)) + complete(&ofl->cmpl); +} + +int rkvpss_register_offline_v1(struct rkvpss_hw_dev *hw) +{ + struct rkvpss_offline_dev *ofl = &hw->ofl_dev; + struct v4l2_device *v4l2_dev; + struct video_device *vfd; + int ret; + + ofl->hw = hw; + v4l2_dev = &ofl->v4l2_dev; + strscpy(v4l2_dev->name, offline_videodev.name, sizeof(v4l2_dev->name)); + ret = v4l2_device_register(hw->dev, v4l2_dev); + if (ret) + return ret; + + mutex_init(&ofl->apilock); + ofl->vfd = offline_videodev; + ofl->mode_sel_en = true; + vfd = &ofl->vfd; + vfd->device_caps = V4L2_CAP_STREAMING; + vfd->lock = &ofl->apilock; + vfd->v4l2_dev = v4l2_dev; + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + v4l2_err(v4l2_dev, "Failed to register video device\n"); + goto unreg_v4l2; + } + video_set_drvdata(vfd, ofl); + INIT_LIST_HEAD(&ofl->list); + INIT_LIST_HEAD(&ofl->cfginfo_list); + mutex_init(&ofl->ofl_lock); + rkvpss_offline_proc_init(ofl); + ofl->pm_need_wait = false; + init_completion(&ofl->pm_cmpl); + return 0; +unreg_v4l2: + mutex_destroy(&ofl->apilock); + v4l2_device_unregister(v4l2_dev); + return ret; +} + +void rkvpss_unregister_offline_v1(struct rkvpss_hw_dev *hw) +{ + mutex_destroy(&hw->ofl_dev.apilock); + video_unregister_device(&hw->ofl_dev.vfd); + v4l2_device_unregister(&hw->ofl_dev.v4l2_dev); + mutex_destroy(&hw->ofl_dev.ofl_lock); + rkvpss_offline_proc_cleanup(&hw->ofl_dev); +} diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_v1.h b/drivers/media/platform/rockchip/vpss/vpss_offline_v1.h new file mode 100644 index 000000000000..877daf8e1fea --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_v1.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_OFFLINE_V1_H +#define _RKVPSS_OFFLINE_V1_H +#define DEV_NUM_MAX 256 +#define UNITE_ENLARGE 16 +#define UNITE_LEFT_ENLARGE 16 + + +#if IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_VPSS_V1) +int rkvpss_register_offline_v1(struct rkvpss_hw_dev *hw); +void rkvpss_unregister_offline_v1(struct rkvpss_hw_dev *hw); +void rkvpss_offline_irq_v1(struct rkvpss_hw_dev *hw, u32 irq); +#else +static inline int rkvpss_register_offline_v1(struct rkvpss_hw_dev *hw) {return -EINVAL; } +static inline void rkvpss_unregister_offline_v1(struct rkvpss_hw_dev *hw) {} +static inline void rkvpss_offline_irq_v1(struct rkvpss_hw_dev *hw, u32 irq) {} +#endif + +#endif + diff --git a/include/uapi/linux/rk-vpss-config.h b/include/uapi/linux/rk-vpss-config.h index f243cac7ebf6..00f69fe72a77 100644 --- a/include/uapi/linux/rk-vpss-config.h +++ b/include/uapi/linux/rk-vpss-config.h @@ -27,6 +27,8 @@ * ioctl RKVPSS_CMD_MODULE_SEL to select function using */ +#define RKVPSS_OUT_V1_MAX 4 + /******vpss(online mode) v4l2 ioctl***************************/ /* set before VIDIOC_S_FMT if dynamically changing output resolution */ #define RKVPSS_CMD_SET_STREAM_MAX_SIZE \ From 2551a5f146b52968d5058d62984cc3c4774f030e Mon Sep 17 00:00:00 2001 From: Tao Huang Date: Wed, 26 Feb 2025 19:18:44 +0800 Subject: [PATCH 14/14] ARM: rockchip_linux_defconfig: Enable CONFIG_WERROR Change-Id: I0a28141853b771b948a19652d7fa846c0fce1466 Signed-off-by: Tao Huang --- arch/arm/configs/rockchip_linux_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/rockchip_linux_defconfig b/arch/arm/configs/rockchip_linux_defconfig index 1937054de218..57e9ca5142ee 100644 --- a/arch/arm/configs/rockchip_linux_defconfig +++ b/arch/arm/configs/rockchip_linux_defconfig @@ -1,3 +1,4 @@ +CONFIG_WERROR=y # CONFIG_LOCALVERSION_AUTO is not set CONFIG_DEFAULT_HOSTNAME="localhost" CONFIG_SYSVIPC=y