diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile index f0fe907e6b89..4784bfb0d8ab 100644 --- a/arch/arm64/boot/dts/rockchip/Makefile +++ b/arch/arm64/boot/dts/rockchip/Makefile @@ -222,6 +222,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-linux.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-linux-amp.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-linux-ipc.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-lt6911uxe.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-lt6911uxc-dual-mipi.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-rk628-hdmi2csi.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb2-lp4-v10.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb2-lp4-v10-edp.dtb diff --git a/arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dts b/arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dts index 7b122c5461f8..fd9b6a2a5386 100644 --- a/arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dts +++ b/arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dts @@ -13,10 +13,6 @@ compatible = "rockchip,px30-evb-ddr3-v10-avb", "rockchip,px30"; }; -&chosen { - bootargs_ext = "androidboot.boot_devices=ff390000.dwmmc,ff3b0000.nandc"; -}; - &dsi { status = "okay"; diff --git a/arch/arm64/boot/dts/rockchip/rk3308b-amp.dtsi b/arch/arm64/boot/dts/rockchip/rk3308b-amp.dtsi index 9b7ec9810f99..a2d39c8ea3ee 100644 --- a/arch/arm64/boot/dts/rockchip/rk3308b-amp.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3308b-amp.dtsi @@ -14,8 +14,8 @@ pinctrl-names = "default"; pinctrl-0 = <&uart1_xfer>; status = "okay"; - amp-cpu-aff-maskbits = <0x0 0x1 0x1 0x2 0x2 0x4 0x3 0x8>; - amp-irqs = ; + amp-cpu-aff-maskbits = /bits/ 64 <0x0 0x1 0x1 0x2 0x2 0x4 0x3 0x8>; + amp-irqs = /bits/ 64 ; }; reserved-memory { diff --git a/arch/arm64/boot/dts/rockchip/rk3326-863-lp3-v10-rkisp1.dts b/arch/arm64/boot/dts/rockchip/rk3326-863-lp3-v10-rkisp1.dts index d80dad694c1d..2d0fa8a8e6cc 100644 --- a/arch/arm64/boot/dts/rockchip/rk3326-863-lp3-v10-rkisp1.dts +++ b/arch/arm64/boot/dts/rockchip/rk3326-863-lp3-v10-rkisp1.dts @@ -11,10 +11,6 @@ compatible = "rockchip,rk3326-863-lp3-v10-rkisp1", "rockchip,rk3326"; }; -&chosen { - bootargs_ext = "androidboot.boot_devices=ff390000.dwmmc,ff3b0000.nandc"; -}; - &i2c2 { status = "okay"; diff --git a/arch/arm64/boot/dts/rockchip/rk3562-amp.dtsi b/arch/arm64/boot/dts/rockchip/rk3562-amp.dtsi index 25f820c68c77..58c9df9d44e6 100644 --- a/arch/arm64/boot/dts/rockchip/rk3562-amp.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3562-amp.dtsi @@ -16,8 +16,8 @@ pinctrl-names = "default"; pinctrl-0 = <&uart7m1_xfer>; - amp-cpu-aff-maskbits = <0x0 0x1 0x1 0x2 0x2 0x4 0x3 0x8>; - amp-irqs = ; + amp-cpu-aff-maskbits = /bits/ 64 <0x0 0x1 0x1 0x2 0x2 0x4 0x3 0x8>; + amp-irqs = /bits/ 64 ; status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi b/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi index 7ef900001f51..dc9ad4236dd3 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi @@ -273,6 +273,7 @@ clock-names = "xvclk"; pinctrl-names = "default"; pinctrl-0 = <&cif_clk>; + power-domains = <&power RK3568_PD_VI>; reset-gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_LOW>; pwdn-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_LOW>; rockchip,grf = <&grf>; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dtsi b/arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dtsi index 4a51275109fb..beba4ab09426 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dtsi @@ -304,6 +304,7 @@ clock-names = "xvclk"; pinctrl-names = "default"; pinctrl-0 = <&cif_clk>; + power-domains = <&power RK3568_PD_VI>; reset-gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_LOW>; pwdn-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_LOW>; rockchip,grf = <&grf>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-lt6911uxc-dual-mipi.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-lt6911uxc-dual-mipi.dts new file mode 100644 index 000000000000..4894f1bb31fa --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-lt6911uxc-dual-mipi.dts @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + * + */ +/dts-v1/; + +#include "rk3588-evb1-lp4.dtsi" +#include "rk3588-android.dtsi" + +/ { + model = "Rockchip RK3588 EVB1 LP4 V10 Board + Rockchip RK3588 EVB V10 Extboard"; + compatible = "rockchip,rk3588-evb1-lp4-lt6911uxc-dual-mipi", "rockchip,rk3588"; + + vcc_mipicsi0: vcc-mipicsi0-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio1 RK_PB1 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&mipicsi0_pwr>; + regulator-name = "vcc_mipicsi0"; + enable-active-high; + regulator-boot-on; + regulator-always-on; + }; + + vcc_mipidcphy0: vcc-mipidcphy0-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio2 RK_PC4 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&mipidcphy0_pwr>; + regulator-name = "vcc_mipidcphy0"; + enable-active-high; + regulator-boot-on; + regulator-always-on; + }; + + ext_cam_clk: external-camera-clock { + compatible = "fixed-clock"; + clock-frequency = <24000000>; + clock-output-names = "CLK_CAMERA_24MHZ"; + #clock-cells = <0>; + }; +}; + +&csi2_dphy0_hw { + status = "okay"; +}; + +&csi2_dphy1_hw { + status = "okay"; +}; + +&csi2_dphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + hdmi_mipi_in: endpoint@1 { + reg = <1>; + remote-endpoint = <<6911uxc_out0>; + data-lanes = <1 2 3 4>; + }; + }; + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidphy0_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi2_csi2_input>; + }; + }; + }; +}; + +&csi2_dphy1 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + hdmi_mipi_in1: endpoint@1 { + reg = <1>; + remote-endpoint = <<6911uxc_out1>; + data-lanes = <1 2 3 4>; + }; + }; + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidphy1_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi0_csi2_input>; + }; + }; + }; +}; + +&i2c3 { + status = "okay"; + + lt6911uxc: lt6911uxc@2b { + compatible = "lontium,lt6911uxc"; + status = "okay"; + reg = <0x2b>; + clocks = <&ext_cam_clk>; + clock-names = "xvclk"; + power-domains = <&power RK3588_PD_VI>; + pinctrl-names = "default"; + pinctrl-0 = <<6911uxc_pin_1>; + interrupt-parent = <&gpio1>; + interrupts = ; + // reset-gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_LOW>; + // power-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_HIGH>; + plugin-det-gpios = <&gpio1 RK_PB4 GPIO_ACTIVE_HIGH>; + rockchip,camera-module-index = <0>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "HDMI-MIPI2"; + rockchip,camera-module-lens-name = "LT6911UXC-2"; + + multi-dev-info { + dev-idx-l = <4>; + dev-idx-r = <2>; + combine-idx = <2>; + pixel-offset = <0>; + dev-num = <2>; + }; + + port { + lt6911uxc_out0: endpoint { + remote-endpoint = <&hdmi_mipi_in>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&i2c5 { + status = "okay"; + + lt6911uxc_1: lt6911uxc_1@2b { + compatible = "lontium,lt6911uxc"; + status = "okay"; + reg = <0x2b>; + clocks = <&ext_cam_clk>; + clock-names = "xvclk"; + power-domains = <&power RK3588_PD_VI>; + pinctrl-names = "default"; + pinctrl-0 = <<6911uxc_pin>; + interrupt-parent = <&gpio1>; + interrupts = ; + // reset-gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_LOW>; + // power-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_HIGH>; + // plugin-det-gpios = <&gpio1 RK_PB4 GPIO_ACTIVE_LOW>; + plugin-det-gpios = <&gpio1 RK_PA1 GPIO_ACTIVE_HIGH>; + rockchip,camera-module-index = <1>; + rockchip,camera-module-facing = "front"; + rockchip,camera-module-name = "HDMI-MIPI0"; + rockchip,camera-module-lens-name = "LT6911UXC-1"; + + multi-dev-info { + dev-idx-l = <1>; + dev-idx-r = <0>; + combine-idx = <0>; + pixel-offset = <0>; + dev-num = <2>; + }; + + port { + lt6911uxc_out1: endpoint { + remote-endpoint = <&hdmi_mipi_in1>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy0 { + status = "okay"; +}; + +&mipi_dcphy1 { + status = "okay"; +}; + +&mipi0_csi2_hw { + status = "okay"; +}; + +&mipi1_csi2_hw { + status = "okay"; +}; + +&mipi2_csi2_hw { + status = "okay"; +}; + +&mipi3_csi2_hw { + status = "okay"; +}; + +&mipi4_csi2_hw { + status = "okay"; +}; + +&mipi5_csi2_hw { + status = "okay"; +}; + +&mipi0_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidphy1_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi_in0>; + }; + }; + }; +}; + +&mipi2_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidphy0_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi_in2>; + }; + }; + }; +}; + +&rkcif_mipi_lvds { + status = "okay"; + + port { + cif_mipi_in0: endpoint { + remote-endpoint = <&mipi0_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds2 { + status = "okay"; + + port { + cif_mipi_in2: endpoint { + remote-endpoint = <&mipi2_csi2_output>; + }; + }; +}; + +&rkcif { + status = "okay"; +}; + +&rkcif_mmu { + status = "okay"; +}; + +&pinctrl { + hdmiin { + lt6911uxc_pin: lt6911uxc-pin { + rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>, + <1 RK_PA1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + lt6911uxc_pin_1: lt6911uxc-pin-1 { + rockchip,pins = <1 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>, + <1 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy0.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy0.dtsi index 6821fa84829d..85007840d55a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy0.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy0.dtsi @@ -12,6 +12,42 @@ clock-frequency = <25000000>; clock-output-names = "max96712-dcphy0-osc"; }; + + max96712_dcphy0_vcc1v2: max96712-dcphy0-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy0_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96712_dcphy0_vcc1v8: max96712-dcphy0-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy0_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96712_dcphy0_poc: max96712-dcphy0-poc { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy0_poc"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &mipi_dcphy0 { @@ -64,8 +100,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio4 RK_PA4 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96712_dcphy0_vcc1v2>; + vcc1v8-supply = <&max96712_dcphy0_vcc1v8>; + poc-supply = <&max96712_dcphy0_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy1.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy1.dtsi index 2b0a04eff63f..9c7b3c7a4677 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy1.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dcphy1.dtsi @@ -12,6 +12,42 @@ clock-frequency = <25000000>; clock-output-names = "max96712-dcphy1-osc"; }; + + max96712_dcphy1_vcc1v2: max96712-dcphy1-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy1_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96712_dcphy1_vcc1v8: max96712-dcphy1-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy1_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96712_dcphy1_poc: max96712-dcphy1-poc { + compatible = "regulator-fixed"; + regulator-name = "max96712_dcphy1_poc"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PA7 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &mipi_dcphy1 { @@ -64,8 +100,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio4 RK_PD1 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PA7 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio4 RK_PD3 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96712_dcphy1_vcc1v2>; + vcc1v8-supply = <&max96712_dcphy1_vcc1v8>; + poc-supply = <&max96712_dcphy1_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy0.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy0.dtsi index b68f8f73607a..18580f723e28 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy0.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy0.dtsi @@ -12,6 +12,42 @@ clock-frequency = <25000000>; clock-output-names = "max96712-dphy0-osc0"; }; + + max96712_dphy0_vcc1v2: max96712-dphy0-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy0_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96712_dphy0_vcc1v8: max96712-dphy0-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy0_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96712_dphy0_poc: max96712-dphy0-poc { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy0_poc"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &csi2_dphy0_hw { @@ -64,8 +100,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96712_dphy0_vcc1v2>; + vcc1v8-supply = <&max96712_dphy0_vcc1v8>; + poc-supply = <&max96712_dphy0_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3.dtsi index 27ab0492cdfc..e146b16365b9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96712-dphy3.dtsi @@ -12,6 +12,41 @@ clock-frequency = <25000000>; clock-output-names = "max96712-dphy3-osc0"; }; + + max96712_dphy3_vcc1v2: max96712-dphy3-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy3_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96712_dphy3_vcc1v8: max96712-dphy3-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy3_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96712_dphy3_poc: max96712-dphy3-poc { + compatible = "regulator-fixed"; + regulator-name = "max96712_dphy3_poc"; + regulator-boot-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &csi2_dphy1_hw { @@ -64,8 +99,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio3 RK_PB4 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96712_dphy3_vcc1v2>; + vcc1v8-supply = <&max96712_dphy3_vcc1v8>; + poc-supply = <&max96712_dphy3_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy0.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy0.dtsi index 45447510b7c1..becc8999e206 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy0.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy0.dtsi @@ -12,6 +12,42 @@ clock-frequency = <25000000>; clock-output-names = "max96722-dphy0-osc0"; }; + + max96722_dphy0_vcc1v2: max96722-dphy0-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy0_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96722_dphy0_vcc1v8: max96722-dphy0-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy0_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96722_dphy0_poc: max96722-dphy0-poc { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy0_poc"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &csi2_dphy0_hw { @@ -64,8 +100,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96722_dphy0_vcc1v2>; + vcc1v8-supply = <&max96722_dphy0_vcc1v8>; + poc-supply = <&max96722_dphy0_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3.dtsi index ad176da3978d..ad1b25ef7b97 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-maxim-max96722-dphy3.dtsi @@ -12,6 +12,42 @@ clock-frequency = <25000000>; clock-output-names = "max96722-dphy3-osc0"; }; + + max96722_dphy3_vcc1v2: max96722-dphy3-vcc1v2 { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy3_vcc1v2"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + startup-delay-us = <850>; + vin-supply = <&vcc5v0_sys>; + }; + + max96722_dphy3_vcc1v8: max96722-dphy3-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy3_vcc1v8"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <200>; + vin-supply = <&vcc_3v3_s3>; + }; + + max96722_dphy3_poc: max96722-dphy3-poc { + compatible = "regulator-fixed"; + regulator-name = "max96722_dphy3_poc"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + enable-active-high; + gpio = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; + startup-delay-us = <1050>; + off-on-delay-us = <515000>; + vin-supply = <&vcc12v_dcin>; + }; }; &csi2_dphy1_hw { @@ -64,8 +100,10 @@ power-domains = <&power RK3588_PD_VI>; rockchip,grf = <&sys_grf>; pwdn-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; - pocen-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; lock-gpios = <&gpio3 RK_PB4 GPIO_ACTIVE_HIGH>; + vcc1v2-supply = <&max96722_dphy3_vcc1v2>; + vcc1v8-supply = <&max96722_dphy3_vcc1v8>; + poc-supply = <&max96722_dphy3_poc>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts index c2986168a6c3..2fe348b486b8 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v22.dts @@ -27,6 +27,8 @@ vin-supply = <&vcc12v_dcin>; pinctrl-names = "default"; pinctrl-0 = <&vcc5v0_buck_en>; + startup-delay-us = <2500>; + off-on-delay-us = <1500>; regulator-state-mem { regulator-on-in-suspend; regulator-suspend-microvolt = <5000000>; @@ -156,6 +158,8 @@ regulator-max-microvolt = <12000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 6 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc12v_dcin>; regulator-state-mem { regulator-off-in-suspend; @@ -172,6 +176,8 @@ regulator-max-microvolt = <12000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 7 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc12v_dcin>; regulator-state-mem { regulator-off-in-suspend; @@ -188,6 +194,8 @@ regulator-max-microvolt = <12000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 8 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc12v_dcin>; regulator-state-mem { regulator-off-in-suspend; @@ -204,6 +212,8 @@ regulator-max-microvolt = <12000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 9 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc12v_dcin>; regulator-state-mem { regulator-off-in-suspend; @@ -220,6 +230,8 @@ regulator-max-microvolt = <5000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 10 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc5v0_usb>; }; @@ -232,6 +244,8 @@ regulator-max-microvolt = <5000000>; enable-active-high; gpio = <&i2c5_nca9539_gpio 11 GPIO_ACTIVE_HIGH>; + startup-delay-us = <2000>; + off-on-delay-us = <16000>; vin-supply = <&vcc5v0_usb>; }; @@ -329,6 +343,14 @@ }; }; +&max96712_dphy3_vcc1v2 { + vin-supply = <&vcc5v0_buck>; +}; + +&max96712_dphy3_poc { + vin-supply = <&camera1_vcc12v_buck>; +}; + &avdd1v8_ddr_pll_s0 { regulator-state-mem { regulator-on-in-suspend; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-evb3-lp4x-v10-sii9022-bt1120-to-hdmi.dts b/arch/arm64/boot/dts/rockchip/rk3588s-evb3-lp4x-v10-sii9022-bt1120-to-hdmi.dts index e2d5f25d53bf..4a03bc67aeb1 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-evb3-lp4x-v10-sii9022-bt1120-to-hdmi.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-evb3-lp4x-v10-sii9022-bt1120-to-hdmi.dts @@ -19,6 +19,13 @@ status = "disabled"; }; +/* + * The pins of gt1x and sii9022 are multiplexed + */ +>1x { + status = "disabled"; +}; + &i2c4 { clock-frequency = <400000>; status = "okay"; @@ -32,6 +39,10 @@ interrupts = ; reset-gpio = <&gpio1 RK_PA7 GPIO_ACTIVE_LOW>; enable-gpio = <&gpio1 RK_PB5 GPIO_ACTIVE_HIGH>; + /* + * MEDIA_BUS_FMT_UYVY8_1X16 for bt1120 + * MEDIA_BUS_FMT_UYVY8_2X8 for bt656 + */ bus-format = ; ports { @@ -60,6 +71,10 @@ &rgb { status = "okay"; pinctrl-names = "default"; + /* + * <&bt1120_pins> for bt1120 + * <&bt656_pins> for bt656 + */ pinctrl-0 = <&bt1120_pins>; ports { diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi index 5008c2727724..cf5842385f07 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi @@ -3019,6 +3019,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c0m0_xfer>; + resets = <&cru SRST_I2C0>, <&cru SRST_P_I2C0>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6004,6 +6006,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c1m0_xfer>; + resets = <&cru SRST_I2C1>, <&cru SRST_P_I2C1>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6017,6 +6021,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c2m0_xfer>; + resets = <&cru SRST_I2C2>, <&cru SRST_P_I2C2>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6030,6 +6036,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c3m0_xfer>; + resets = <&cru SRST_I2C3>, <&cru SRST_P_I2C3>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6043,6 +6051,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c4m0_xfer>; + resets = <&cru SRST_I2C4>, <&cru SRST_P_I2C4>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6056,6 +6066,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c5m0_xfer>; + resets = <&cru SRST_I2C5>, <&cru SRST_P_I2C5>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6483,6 +6495,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c6m0_xfer>; + resets = <&cru SRST_I2C6>, <&cru SRST_P_I2C6>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6496,6 +6510,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c7m0_xfer>; + resets = <&cru SRST_I2C7>, <&cru SRST_P_I2C7>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -6509,6 +6525,8 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&i2c8m0_xfer>; + resets = <&cru SRST_I2C8>, <&cru SRST_P_I2C8>; + reset-names = "i2c", "apb"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; diff --git a/arch/arm64/configs/rockchip_defconfig b/arch/arm64/configs/rockchip_defconfig index 805f4aa3a520..b9d002aee0ec 100644 --- a/arch/arm64/configs/rockchip_defconfig +++ b/arch/arm64/configs/rockchip_defconfig @@ -476,6 +476,7 @@ CONFIG_GS_LSM303D=y CONFIG_LSM330_ACC=y CONFIG_BMA2XX_ACC=y CONFIG_GS_DA223=y +CONFIG_ICM4260X_ACC=y CONFIG_COMPASS_DEVICE=y CONFIG_COMPASS_AK8975=y CONFIG_COMPASS_AK8963=y @@ -486,6 +487,7 @@ CONFIG_GYRO_EWTSA=y CONFIG_GYRO_MPU6500=y CONFIG_GYRO_MPU6880=y CONFIG_GYRO_LSM330=y +CONFIG_GYRO_ICM4260X=y CONFIG_LIGHT_DEVICE=y CONFIG_LS_CM3217=y CONFIG_LS_CM3218=y @@ -521,6 +523,7 @@ CONFIG_SPI_SPIDEV=y CONFIG_PINCTRL_RK805=y CONFIG_PINCTRL_RK806=y CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_SYSCON=y CONFIG_POWER_RESET_GPIO=y CONFIG_SYSCON_REBOOT_MODE=y CONFIG_TEST_POWER=y diff --git a/arch/arm64/configs/rockchip_gki.config b/arch/arm64/configs/rockchip_gki.config index 8699efba85e8..4c90904b2660 100644 --- a/arch/arm64/configs/rockchip_gki.config +++ b/arch/arm64/configs/rockchip_gki.config @@ -83,6 +83,7 @@ CONFIG_GS_SC7A20=m CONFIG_GS_SC7A30=m CONFIG_GYROSCOPE_DEVICE=m CONFIG_GYRO_EWTSA=m +CONFIG_GYRO_ICM4260X=m CONFIG_GYRO_L3G20D=m CONFIG_GYRO_L3G4200D=m CONFIG_GYRO_LSM330=m @@ -137,6 +138,7 @@ CONFIG_I2C_CHARDEV=m CONFIG_I2C_GPIO=m CONFIG_I2C_HID_OF=m CONFIG_I2C_RK3X=m +CONFIG_ICM4260X_ACC=m CONFIG_IEP=m CONFIG_IIO_BUFFER_CB=m CONFIG_INPUT_RK805_PWRKEY=m diff --git a/arch/arm64/configs/rockchip_linux_defconfig b/arch/arm64/configs/rockchip_linux_defconfig index 9e94d63ee4b5..38804161a4b5 100644 --- a/arch/arm64/configs/rockchip_linux_defconfig +++ b/arch/arm64/configs/rockchip_linux_defconfig @@ -254,6 +254,7 @@ CONFIG_PINCTRL_RK805=y CONFIG_PINCTRL_RK806=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_SYSCON=y CONFIG_POWER_RESET_GPIO=y CONFIG_POWER_RESET_GPIO_RESTART=y CONFIG_SYSCON_REBOOT_MODE=y diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 0ff9bd2fa5e7..255a1f1bfaf2 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -541,11 +541,9 @@ struct dma_pl330_desc { /* For cyclic capability */ bool cyclic; size_t num_periods; -#ifdef CONFIG_NO_GKI - /* interlace size */ - unsigned int src_interlace_size; - unsigned int dst_interlace_size; -#endif + + /* interleaved size */ + struct data_chunk sgl; }; struct _xfer_spec { @@ -1204,7 +1202,7 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, const struct _xfer_spec *pxs, int cyc, enum pl330_cond cond) { - int off = 0; + int off = 0, i = 0, burstn = 1; /* * do FLUSHP at beginning to clear any stale dma requests before the @@ -1212,30 +1210,36 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, */ if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); + + if (pxs->desc->sgl.size) { + WARN_ON(BYTE_MOD_BURST_LEN(pxs->desc->sgl.size, pxs->ccr)); + burstn = BYTE_TO_BURST(pxs->desc->sgl.size, pxs->ccr); + } + while (cyc--) { - off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); - off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, - pxs->desc->peri); - off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, - pxs->desc->peri); -#ifdef CONFIG_NO_GKI + for (i = 0; i < burstn; i++) { + off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); + off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, + pxs->desc->peri); + off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, + pxs->desc->peri); + } + switch (pxs->desc->rqtype) { case DMA_DEV_TO_MEM: - - if (pxs->desc->dst_interlace_size) + if (pxs->desc->sgl.dst_icg) off += _emit_ADDH(dry_run, &buf[off], DST, - pxs->desc->dst_interlace_size); + pxs->desc->sgl.dst_icg); break; case DMA_MEM_TO_DEV: - if (pxs->desc->src_interlace_size) + if (pxs->desc->sgl.src_icg) off += _emit_ADDH(dry_run, &buf[off], SRC, - pxs->desc->src_interlace_size); + pxs->desc->sgl.src_icg); break; default: WARN_ON(1); break; } -#endif } return off; @@ -1450,9 +1454,7 @@ static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], off += _emit_LPEND(dry_run, &buf[off], &lpend); } -#ifdef CONFIG_NO_GKI - if (!pxs->desc->src_interlace_size && - !pxs->desc->dst_interlace_size) { + if (!pxs->desc->sgl.src_icg && !pxs->desc->sgl.dst_icg) { num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); if (num_dregs) { @@ -1460,14 +1462,6 @@ static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); } } -#else - num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); - - if (num_dregs) { - off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); - off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); - } -#endif off += _emit_SEV(dry_run, &buf[off], ev); @@ -1535,26 +1529,18 @@ static inline int _setup_loops(struct pl330_dmac *pl330, BRST_SIZE(ccr); int off = 0; -#ifdef CONFIG_NO_GKI - if (pxs->desc->rqtype == DMA_DEV_TO_MEM) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) + - pxs->desc->dst_interlace_size); - else if (pxs->desc->rqtype == DMA_MEM_TO_DEV) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) + - pxs->desc->src_interlace_size); -#endif + if (pxs->desc->sgl.size) + bursts = x->bytes / pxs->desc->sgl.size; + while (bursts) { c = bursts; off += _loop(pl330, dry_run, &buf[off], &c, pxs); bursts -= c; } -#ifdef CONFIG_NO_GKI - if (!pxs->desc->src_interlace_size && - !pxs->desc->dst_interlace_size) + + if (!pxs->desc->sgl.src_icg && !pxs->desc->sgl.dst_icg) off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); -#else - off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); -#endif + return off; } @@ -1585,14 +1571,9 @@ static inline int _setup_xfer_cyclic(struct pl330_dmac *pl330, unsigned long bursts = BYTE_TO_BURST(x->bytes, ccr); int off = 0; -#ifdef CONFIG_NO_GKI - if (pxs->desc->rqtype == DMA_DEV_TO_MEM) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) - + pxs->desc->dst_interlace_size); - else if (pxs->desc->rqtype == DMA_MEM_TO_DEV) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) - + pxs->desc->src_interlace_size); -#endif + if (pxs->desc->sgl.size) + bursts = x->bytes / pxs->desc->sgl.size; + /* Setup Loop(s) */ off += _loop_cyclic(pl330, dry_run, &buf[off], bursts, pxs, ev); @@ -2452,10 +2433,6 @@ static int pl330_config_write(struct dma_chan *chan, pch->fifo_addr = slave_config->dst_addr; if (slave_config->dst_addr_width) pch->burst_sz = __ffs(slave_config->dst_addr_width); -#ifdef CONFIG_NO_GKI - if (slave_config->src_interlace_size) - pch->slave_config.src_interlace_size = slave_config->src_interlace_size; -#endif pch->burst_len = fixup_burst_len(slave_config->dst_maxburst, pch->dmac->quirks); } else if (direction == DMA_DEV_TO_MEM) { @@ -2463,10 +2440,6 @@ static int pl330_config_write(struct dma_chan *chan, pch->fifo_addr = slave_config->src_addr; if (slave_config->src_addr_width) pch->burst_sz = __ffs(slave_config->src_addr_width); -#ifdef CONFIG_NO_GKI - if (slave_config->dst_interlace_size) - pch->slave_config.dst_interlace_size = slave_config->dst_interlace_size; -#endif pch->burst_len = fixup_burst_len(slave_config->src_maxburst, pch->dmac->quirks); } @@ -2819,6 +2792,10 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) desc->cyclic = false; desc->num_periods = 1; + desc->sgl.size = 0; + desc->sgl.src_icg = 0; + desc->sgl.dst_icg = 0; + dma_async_tx_descriptor_init(&desc->txd, &pch->chan); return desc; @@ -2933,10 +2910,78 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( desc->cyclic = true; desc->num_periods = len / period_len; + + return &desc->txd; +} + +static struct dma_async_tx_descriptor *pl330_prep_interleaved_dma( + struct dma_chan *chan, struct dma_interleaved_template *xt, + unsigned long flags) +{ + struct dma_pl330_desc *desc = NULL; + struct dma_pl330_chan *pch = to_pchan(chan); + dma_addr_t dst = 0, src = 0; + size_t size, src_icg, dst_icg, period_bytes, buffer_bytes; + size_t nump = 0, numf = 0; + + if (!xt->numf || !xt->sgl[0].size || xt->frame_size != 1) + return NULL; + #ifdef CONFIG_NO_GKI - desc->src_interlace_size = pch->slave_config.src_interlace_size; - desc->dst_interlace_size = pch->slave_config.dst_interlace_size; + nump = xt->nump; #endif + numf = xt->numf; + size = xt->sgl[0].size; + period_bytes = size * nump; + buffer_bytes = size * numf; + + if (flags & DMA_PREP_REPEAT && (!nump || (numf % nump))) + return NULL; + + src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); + dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); + + pl330_config_write(chan, &pch->slave_config, xt->dir); + + if (!pl330_prep_slave_fifo(pch, xt->dir)) + return NULL; + + desc = pl330_get_desc(pch); + if (!desc) { + dev_err(chan->device->dev, "Failed to get desc\n"); + return NULL; + } + + if (xt->dir == DMA_MEM_TO_DEV) { + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; + src = xt->src_start; + dst = pch->fifo_dma; + } else { + desc->rqcfg.src_inc = 0; + desc->rqcfg.dst_inc = 1; + src = pch->fifo_dma; + dst = xt->dst_start; + } + + desc->rqtype = xt->dir; + desc->rqcfg.brst_size = pch->burst_sz; + desc->rqcfg.brst_len = pch->burst_len; + desc->bytes_requested = buffer_bytes; + desc->sgl.size = size; + desc->sgl.src_icg = src_icg; + desc->sgl.dst_icg = dst_icg; + + if (flags & DMA_PREP_REPEAT) { + desc->cyclic = true; + desc->num_periods = numf / nump; + fill_px(&desc->px, dst, src, period_bytes); + } else { + fill_px(&desc->px, dst, src, buffer_bytes); + } + + dev_dbg(chan->device->dev, "size: %zu, src_icg: %zu, dst_icg: %zu, nump: %zu, numf: %zu\n", + size, src_icg, dst_icg, nump, numf); return &desc->txd; } @@ -3068,10 +3113,6 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, desc->rqcfg.brst_len = pch->burst_len; desc->rqtype = direction; desc->bytes_requested = sg_dma_len(sg); -#ifdef CONFIG_NO_GKI - desc->src_interlace_size = pch->slave_config.src_interlace_size; - desc->dst_interlace_size = pch->slave_config.dst_interlace_size; -#endif } /* Return the last desc in the chain */ @@ -3306,12 +3347,16 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) dma_cap_set(DMA_SLAVE, pd->cap_mask); dma_cap_set(DMA_CYCLIC, pd->cap_mask); dma_cap_set(DMA_PRIVATE, pd->cap_mask); + dma_cap_set(DMA_INTERLEAVE, pd->cap_mask); + dma_cap_set(DMA_REPEAT, pd->cap_mask); + dma_cap_set(DMA_LOAD_EOT, pd->cap_mask); } pd->device_alloc_chan_resources = pl330_alloc_chan_resources; pd->device_free_chan_resources = pl330_free_chan_resources; pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy; pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic; + pd->device_prep_interleaved_dma = pl330_prep_interleaved_dma; pd->device_tx_status = pl330_tx_status; pd->device_prep_slave_sg = pl330_prep_slave_sg; pd->device_config = pl330_config; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 0fbc97b5f2d9..b486f1b57128 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -2554,9 +2554,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, vmode->previous_pixelclock = vmode->mpixelclock; vmode->mpixelclock = mode->crtc_clock * 1000; - if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == - DRM_MODE_FLAG_3D_FRAME_PACKING) - vmode->mpixelclock *= 2; dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); vmode->previous_tmdsclock = vmode->mtmdsclock; diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index da241669a9f3..0c4a0cc400ae 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #define REG_IEN 0x18 /* interrupt enable */ #define REG_IPD 0x1c /* interrupt pending */ #define REG_FCNT 0x20 /* finished count */ +#define REG_SCL_OE_DB 0x24 /* Slave hold scl debounce */ #define REG_CON1 0x228 /* control register1 */ /* Data buffer offsets */ @@ -87,6 +89,7 @@ enum { #define REG_INT_START BIT(4) /* START condition generated */ #define REG_INT_STOP BIT(5) /* STOP condition generated */ #define REG_INT_NAKRCV BIT(6) /* NACK received */ +#define REG_INT_SLV_HDSCL BIT(7) /* slave hold scl */ #define REG_INT_ALL 0xff /* Disable i2c all irqs */ @@ -97,7 +100,7 @@ enum { #define REG_CON1_NACK_AUTO_STOP BIT(2) /* Constants */ -#define WAIT_TIMEOUT 1000 /* ms */ +#define WAIT_TIMEOUT 200 /* ms */ #define DEFAULT_SCL_RATE (100 * 1000) /* Hz */ /** @@ -225,6 +228,9 @@ struct rk3x_i2c { struct notifier_block clk_rate_nb; bool autostop_supported; + struct reset_control *reset; + struct reset_control *reset_apb; + /* Settings */ struct i2c_timings t; @@ -307,6 +313,13 @@ static bool rk3x_i2c_auto_stop(struct rk3x_i2c *i2c) if (len > 32) goto out; + /* For tx mode, one byte of the device address also needs to be counted, + * if the data length is equal to 32, which is actually 33 bytes, it would + * need to be divided into two parts, and needs to jump out of autostop. + */ + if (i2c->msg->len == 32 && i2c->mode == REG_CON_MOD_TX && !i2c->processed) + goto out; + i2c->state = STATE_STOP; con1 |= REG_CON1_TRANSFER_AUTO_STOP | REG_CON1_AUTO_STOP; @@ -983,9 +996,10 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) { struct i2c_timings *t = &i2c->t; struct rk3x_i2c_calced_timings calc; + unsigned long period, time_hold = (WAIT_TIMEOUT / 2) * 1000000; u64 t_low_ns, t_high_ns; unsigned long flags; - u32 val; + u32 val, cnt; int ret; ret = i2c->soc_data->calc_timings(clk_rate, t, &calc); @@ -1000,6 +1014,10 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) i2c_writel(i2c, val, REG_CON); i2c_writel(i2c, (calc.div_high << 16) | (calc.div_low & 0xffff), REG_CLKDIV); + + period = DIV_ROUND_UP(1000000000, clk_rate); + cnt = DIV_ROUND_UP(time_hold, period); + i2c_writel(i2c, cnt, REG_SCL_OE_DB); spin_unlock_irqrestore(&i2c->lock, flags); clk_disable(i2c->pclk); @@ -1165,12 +1183,30 @@ static int rk3x_i2c_wait_xfer_poll(struct rk3x_i2c *i2c, unsigned long xfer_time return !i2c->busy; } +/* + * Reset i2c controller, reset all i2c registers. + */ +static void rk3x_i2c_reset_controller(struct rk3x_i2c *i2c) +{ + if (!IS_ERR_OR_NULL(i2c->reset)) { + reset_control_assert(i2c->reset); + udelay(10); + reset_control_deassert(i2c->reset); + } + + if (!IS_ERR_OR_NULL(i2c->reset_apb)) { + reset_control_assert(i2c->reset_apb); + udelay(10); + reset_control_deassert(i2c->reset_apb); + } +} + static int rk3x_i2c_xfer_common(struct i2c_adapter *adap, struct i2c_msg *msgs, int num, bool polling) { struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data; unsigned long timeout, flags; - u32 val; + u32 val, ipd = 0; int ret = 0; int i; @@ -1189,7 +1225,7 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap, * rk3x_i2c_setup()). */ for (i = 0; i < num; i += ret) { - unsigned long xfer_time = 100; + unsigned long xfer_time = WAIT_TIMEOUT; int len; ret = rk3x_i2c_setup(i2c, msgs + i, num - i); @@ -1227,8 +1263,9 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap, spin_lock_irqsave(&i2c->lock, flags); if (timeout == 0) { + ipd = i2c_readl(i2c, REG_IPD); dev_err(i2c->dev, "timeout, ipd: 0x%02x, state: %d\n", - i2c_readl(i2c, REG_IPD), i2c->state); + ipd, i2c->state); /* Force a STOP condition without interrupt */ rk3x_i2c_disable_irq(i2c); @@ -1256,6 +1293,12 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap, spin_unlock_irqrestore(&i2c->lock, flags); + if ((ret == -ETIMEDOUT) && (ipd & REG_INT_SLV_HDSCL)) { + rk3x_i2c_reset_controller(i2c); + dev_err(i2c->dev, "SCL hold by slave, check your device.\n"); + rk3x_i2c_adapt_div(i2c, clk_get_rate(i2c->clk)); + } + return ret < 0 ? ret : num; } @@ -1602,6 +1645,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2c); + i2c->reset = devm_reset_control_get(&pdev->dev, "i2c"); if (!has_acpi_companion(&pdev->dev)) { if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) { /* Only one clock to use for bus clock and peripheral clock */ @@ -1610,6 +1654,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev) } else { i2c->clk = devm_clk_get(&pdev->dev, "i2c"); i2c->pclk = devm_clk_get(&pdev->dev, "pclk"); + i2c->reset_apb = devm_reset_control_get(&pdev->dev, "apb"); } if (IS_ERR(i2c->clk)) diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index 0799f0129d08..7bd7ba55007e 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -68,6 +69,8 @@ struct rockchip_saradc { struct clk *clk; struct completion completion; struct regulator *vref; + /* lock to protect against multiple access to the device */ + struct mutex lock; int uv_vref; struct reset_control *reset; const struct rockchip_saradc_data *data; @@ -189,22 +192,22 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev, #endif switch (mask) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); + mutex_lock(&info->lock); if (info->suspended) { - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); return -EBUSY; } ret = rockchip_saradc_conversion(info, chan); if (ret) { rockchip_saradc_power_down(info); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); return ret; } *val = info->last_val; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: /* It is a dummy regulator */ @@ -476,7 +479,7 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) int ret; int i, j = 0; - mutex_lock(&i_dev->mlock); + mutex_lock(&info->lock); for_each_set_bit(i, i_dev->active_scan_mask, i_dev->masklength) { const struct iio_chan_spec *chan = &i_dev->channels[i]; @@ -493,7 +496,7 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) iio_push_to_buffers_with_timestamp(i_dev, &data, iio_get_time_ns(i_dev)); out: - mutex_unlock(&i_dev->mlock); + mutex_unlock(&info->lock); iio_trigger_notify_done(i_dev->trig); @@ -779,6 +782,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev) return ret; } #endif + mutex_init(&info->lock); + return devm_iio_device_register(&pdev->dev, indio_dev); } @@ -788,14 +793,14 @@ static int rockchip_saradc_suspend(struct device *dev) struct rockchip_saradc *info = iio_priv(indio_dev); /* Avoid reading saradc when suspending */ - mutex_lock(&indio_dev->mlock); + mutex_lock(&info->lock); clk_disable_unprepare(info->clk); clk_disable_unprepare(info->pclk); regulator_disable(info->vref); info->suspended = true; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); return 0; } diff --git a/drivers/input/sensors/accel/Kconfig b/drivers/input/sensors/accel/Kconfig index 4576b8ccdb69..fb10b4c50c83 100644 --- a/drivers/input/sensors/accel/Kconfig +++ b/drivers/input/sensors/accel/Kconfig @@ -158,6 +158,12 @@ config ICM2060X_ACC To have support for your specific gsesnor you will have to select the proper drivers which depend on this option. +config ICM4260X_ACC + tristate "gsensor icm4260x" + help + To have support for your specific gsesnor you will have to + select the proper drivers which depend on this option. + config IAM20680_ACC tristate "gsensor iam20680" default n diff --git a/drivers/input/sensors/accel/Makefile b/drivers/input/sensors/accel/Makefile index 96f6cc6ee8e9..67e0bcdde3da 100644 --- a/drivers/input/sensors/accel/Makefile +++ b/drivers/input/sensors/accel/Makefile @@ -21,5 +21,6 @@ obj-$(CONFIG_GS_DA215S) += da215s/ obj-$(CONFIG_GS_DA223) += da223.o obj-$(CONFIG_GS_DA228E) += da228e/ obj-$(CONFIG_ICM2060X_ACC) += icm2060x_acc.o +obj-$(CONFIG_ICM4260X_ACC) += icm4260x_acc.o da223-y := da223_cust.o da223_core.o obj-$(CONFIG_IAM20680_ACC) += iam20680_acc.o diff --git a/drivers/input/sensors/accel/icm4260x_acc.c b/drivers/input/sensors/accel/icm4260x_acc.c new file mode 100644 index 000000000000..afc67123a844 --- /dev/null +++ b/drivers/input/sensors/accel/icm4260x_acc.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Rockchip Co.,Ltd. + * Author: Wangqiang Guo + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include + +/** + * icm4260x_set_idle() - Set Idle bit in PWR_MGMT_0 register + * @client: struct i2c_client.. + * + * Set ACCEL_LP_CLK_SEL as well when necessary with a proper wait + * + * Return: 0 when successful. + */ +static int icm4260x_set_idle(struct i2c_client *client) +{ + u8 reg_pwr_mgmt_0; + u8 d; + int ret = 0; + + reg_pwr_mgmt_0 = sensor_read_reg(client, ICM4260X_PWR_MGMT_0); + /* set Idle bit. + * when accel LPM is already enabled, set ACCEL_LP_CLK_SEL bit as well. + */ + d = reg_pwr_mgmt_0; + d |= BIT_IDLE; + if ((d & BIT_ACCEL_MODE_MASK) == BIT_ACCEL_MODE_LPM) + d |= BIT_ACCEL_LP_CLK_SEL; + + ret = sensor_write_reg(client, ICM4260X_PWR_MGMT_0, d); + usleep_range(20, 21); + + return ret; +} + +/** + * icm4260x_mreg_read() - Multiple byte read from MREG area. + * @client: struct i2c_client. + * @addr: MREG register start address including bank in upper byte. + * @len: length to read in byte. + * @data: pointer to store read data. + * + * Return: 0 when successful. + */ +static int icm4260x_mreg_read(struct i2c_client *client, int addr, int len, u8 *data) +{ + int ret; + u8 reg_pwr_mgmt_0; + + reg_pwr_mgmt_0 = sensor_read_reg(client, ICM4260X_PWR_MGMT_0); + + ret = icm4260x_set_idle(client); + if (ret) + return ret; + + ret = sensor_write_reg(client, ICM4260X_BLK_SEL_R, (addr >> 8) & 0xff); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + if (ret) + goto restore_bank; + + ret = sensor_write_reg(client, ICM4260X_MADDR_R, addr & 0xff); + usleep_range(INV_ICM42607_MADDR_WAIT_US, + INV_ICM42607_MADDR_WAIT_US + 1); + if (ret) + goto restore_bank; + + *data = ICM4260X_M_R; + ret = sensor_rx_data(client, data, len); + usleep_range(INV_ICM42607_M_RW_WAIT_US, + INV_ICM42607_M_RW_WAIT_US + 1); + if (ret) + goto restore_bank; + +restore_bank: + ret |= sensor_write_reg(client, ICM4260X_BLK_SEL_R, 0); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + + ret |= sensor_write_reg(client, ICM4260X_PWR_MGMT_0, reg_pwr_mgmt_0); + + return ret; +} + +/** + * icm4260x_mreg_single_write() - Single byte write to MREG area. + * @client: struct i2c_client. + * @addr: MREG register address including bank in upper byte. + * @data: data to write. + * + * Return: 0 when successful. + */ +static int icm4260x_mreg_single_write(struct i2c_client *client, int addr, u8 data) +{ + int ret; + u8 reg_pwr_mgmt_0; + + reg_pwr_mgmt_0 = sensor_read_reg(client, ICM4260X_PWR_MGMT_0); + + ret = icm4260x_set_idle(client); + if (ret) + return ret; + + ret = sensor_write_reg(client, ICM4260X_BLK_SEL_W, (addr >> 8) & 0xff); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + if (ret) + goto restore_bank; + + ret = sensor_write_reg(client, ICM4260X_MADDR_W, addr & 0xff); + usleep_range(INV_ICM42607_MADDR_WAIT_US, + INV_ICM42607_MADDR_WAIT_US + 1); + if (ret) + goto restore_bank; + + ret = sensor_write_reg(client, ICM4260X_M_W, data); + usleep_range(INV_ICM42607_M_RW_WAIT_US, + INV_ICM42607_M_RW_WAIT_US + 1); + if (ret) + goto restore_bank; + +restore_bank: + ret |= sensor_write_reg(client, ICM4260X_BLK_SEL_W, 0); + usleep_range(INV_ICM42607_BLK_SEL_WAIT_US, + INV_ICM42607_BLK_SEL_WAIT_US + 1); + + ret |= sensor_write_reg(client, ICM4260X_PWR_MGMT_0, reg_pwr_mgmt_0); + + return ret; +} + +/* + * OTP reload procedure. + */ +static int icm4260x_otp_reload(struct i2c_client *client) +{ + int ret; + u8 rb = 0; + + /* set idle bit */ + ret = icm4260x_set_idle(client); + if (ret) + return ret; + + /* Set OTP_COPY_MODE to 2'b01 */ + ret = icm4260x_mreg_read(client, ICM4260X_OTP_CONFIG_MREG_TOP1, 1, &rb); + if (ret) + return ret; + rb &= ~OTP_COPY_MODE_MASK; + rb |= BIT_OTP_COPY_NORMAL; + ret = icm4260x_mreg_single_write(client, ICM4260X_OTP_CONFIG_MREG_TOP1, rb); + if (ret) + return ret; + + /* set OTP_PWR_DOWN to 1'b0 and wait for 300us */ + ret = icm4260x_mreg_read(client, ICM4260X_OTP_CTRL7_MREG_OTP, 1, &rb); + if (ret) + return ret; + rb &= ~BIT_OTP_PWR_DOWN; + ret = icm4260x_mreg_single_write(client, ICM4260X_OTP_CTRL7_MREG_OTP, rb); + if (ret) + return ret; + usleep_range(300, 400); + + /* set OTP_RELOAD to 1'b1 and wait for 280us */ + ret = icm4260x_mreg_read(client, ICM4260X_OTP_CTRL7_MREG_OTP, 1, &rb); + if (ret) + return ret; + rb |= BIT_OTP_RELOAD; + ret = icm4260x_mreg_single_write(client, ICM4260X_OTP_CTRL7_MREG_OTP, rb); + if (ret) + return ret; + usleep_range(280, 380); + + return 0; +} + +static int sensor_active(struct i2c_client *client, int enable, int rate) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + u8 status = 0; + + sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg); + + if (!enable) { + status = (0xff & ~BIT_ACCEL_MODE_MASK); + sensor->ops->ctrl_data &= status; + } else { + status = BIT_ACCEL_MODE_LNM; + sensor->ops->ctrl_data |= status; + sensor->ops->ctrl_data &= ~BIT_IDLE; + } + + result = sensor_write_reg(client, sensor->ops->ctrl_reg, + sensor->ops->ctrl_data); + if (result) { + dev_err(&client->dev, + "%s: fail to set pwr_mgmt0(%d)\n", __func__, result); + return result; + } + usleep_range(250, 260); + + return result; +} + +/* + * write POR value + */ + +static int icm4260x_set_default_register(struct i2c_client *client) +{ + int status = 0; + + status |= sensor_write_reg(client, ICM4260X_GYRO_CONFIG0, 0x06); + status |= sensor_write_reg(client, ICM4260X_ACCEL_CONFIG0, 0x06); + status |= sensor_write_reg(client, ICM4260X_APEX_CONFIG0, 0x08); + status |= sensor_write_reg(client, ICM4260X_APEX_CONFIG1, 0x02); + status |= sensor_write_reg(client, ICM4260X_WOM_CONFIG, 0); + status |= sensor_write_reg(client, ICM4260X_FIFO_CONFIG1, 0x01); + status |= sensor_write_reg(client, ICM4260X_FIFO_CONFIG2, 0); + status |= sensor_write_reg(client, ICM4260X_FIFO_CONFIG3, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_FIFO_CONFIG5_MREG_TOP1, 0x20); + status |= icm4260x_mreg_single_write(client, ICM4260X_ST_CONFIG_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE7_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE8_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE9_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_INT_SOURCE10_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG2_MREG_TOP1, 0xA2); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG3_MREG_TOP1, 0x85); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG4_MREG_TOP1, 0x51); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG5_MREG_TOP1, 0x80); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG9_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG10_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG11_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_ACCEL_WOM_X_THR_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_ACCEL_WOM_Y_THR_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_ACCEL_WOM_Z_THR_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER0_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER1_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER2_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER3_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER4_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER5_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER6_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER7_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_GOS_USER8_MREG_TOP1, 0); + status |= icm4260x_mreg_single_write(client, ICM4260X_APEX_CONFIG12_MREG_TOP1, 0); + + if (status) + return -EIO; + + return 0; +} + +static int sensor_init(struct i2c_client *client) +{ + int ret = 0; + u8 device_id = 0, value = 0; + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + device_id = sensor_read_reg(client, ICM4260X_WHO_AM_I); + if (device_id != ICM42607_DEVICE_ID) { + dev_err(&client->dev, "%s: check id err, read_id: %d\n", + __func__, device_id); + return -1; + } + + ret = icm4260x_otp_reload(client); + if (ret) { + dev_err(&client->dev, + "ICM4260X OTP reload error,ret: %d!\n", ret); + return ret; + } + + + ret = icm4260x_set_default_register(client); + if (ret) { + dev_err(&client->dev, + "set ICM4260X default_register error,ret: %d!\n", ret); + return ret; + } + + /* SPI or I2C only + * FIFO count : byte mode, big endian + * sensor data : big endian + */ + value |= BIT_FIFO_COUNT_ENDIAN; + value |= BIT_SENSOR_DATA_ENDIAN; + ret = sensor_write_reg(client, ICM4260X_INTF_CONFIG0, value); + if (ret) + return ret; + + /* configure clock */ + value = BIT_CLK_SEL_PLL | BIT_I3C_SDR_EN | BIT_I3C_DDR_EN; + ret = sensor_write_reg(client, ICM4260X_INTF_CONFIG1, value); + if (ret) + return ret; + + /* INT pin configuration */ + /* + * value = (INT_POLARITY << SHIFT_INT1_POLARITY) | + * (INT_DRIVE_CIRCUIT << SHIFT_INT1_DRIVE_CIRCUIT) | + * (INT_MODE << SHIFT_INT1_MODE); + * ret = sensor_write_reg(client, ICM4260X_INT_CONFIG_REG, value); + * if (ret) + * return ret; + */ + + /* disable sensors */ + ret = sensor_write_reg(client, ICM4260X_PWR_MGMT_0, 0); + if (ret) + return ret; + + /* set Full scale select for accelerometer UI interface output*/ + value = sensor_read_reg(client, ICM4260X_ACCEL_CONFIG0); + value &= ~BIT_ACCEL_FSR; + value |= ACCEL_FS_SEL << SHIFT_ACCEL_FS_SEL; + ret = sensor_write_reg(client, ICM4260X_ACCEL_CONFIG0, value); + if (ret) + return ret; + + /* turn on accelerometer*/ + ret = sensor->ops->active(client, 0, sensor->pdata->poll_delay_ms); + if (ret) { + dev_err(&client->dev, + "%s: fail to active sensor(%d)\n", __func__, ret); + return ret; + } + + return ret; +} + +static int gsensor_report_value(struct i2c_client *client, + struct sensor_axis *axis) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + if (sensor->status_cur == SENSOR_ON) { + /* Report acceleration sensor information */ + input_report_abs(sensor->input_dev, ABS_X, axis->x); + input_report_abs(sensor->input_dev, ABS_Y, axis->y); + input_report_abs(sensor->input_dev, ABS_Z, axis->z); + input_sync(sensor->input_dev); + } + + return 0; +} + +static int sensor_report_value(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_platform_data *pdata = sensor->pdata; + int ret = 0; + short x, y, z; + struct sensor_axis axis; + u8 buffer[6] = {0}; + + if (sensor->ops->read_len < 6) { + dev_err(&client->dev, "%s: length is error, len = %d\n", + __func__, sensor->ops->read_len); + return -EINVAL; + } + + /* Data bytes from hardware xH, xL, yH, yL, zH, zL */ + *buffer = sensor->ops->read_reg; + ret = sensor_rx_data(client, buffer, sensor->ops->read_len); + if (ret < 0) { + dev_err(&client->dev, + "%s: read data failed, ret = %d\n", __func__, ret); + return ret; + } + x = ((buffer[0] << 8) & 0xff00) + (buffer[1] & 0xFF); + y = ((buffer[2] << 8) & 0xff00) + (buffer[3] & 0xFF); + z = ((buffer[4] << 8) & 0xff00) + (buffer[5] & 0xFF); + + //printk("%s,x:%d, y:%d, z:%d\n", __func__, x, y, z); + axis.x = (pdata->orientation[0]) * x + (pdata->orientation[1]) * y + + (pdata->orientation[2]) * z; + axis.y = (pdata->orientation[3]) * x + (pdata->orientation[4]) * y + + (pdata->orientation[5]) * z; + axis.z = (pdata->orientation[6]) * x + (pdata->orientation[7]) * y + + (pdata->orientation[8]) * z; + + gsensor_report_value(client, &axis); + + mutex_lock(&(sensor->data_mutex)); + sensor->axis = axis; + mutex_unlock(&(sensor->data_mutex)); + + return ret; +} + +static struct sensor_operate gsensor_icm4260x_ops = { + .name = "icm4260x_acc", + .type = SENSOR_TYPE_ACCEL, + .id_i2c = ACCEL_ID_ICM4260X, + .read_reg = ICM4260X_ACCEL_DATA_X0, + .read_len = 6, + .id_reg = SENSOR_UNKNOW_DATA, + .id_data = SENSOR_UNKNOW_DATA, + .precision = ICM4260X_PRECISION, + .ctrl_reg = ICM4260X_PWR_MGMT_0, + .int_status_reg = ICM4260X_INT_STATUS, + .range = {-32768, 32768}, + .trig = IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + .active = sensor_active, + .init = sensor_init, + .report = sensor_report_value, +}; + +/****************operate according to sensor chip:end************/ +static int gsensor_icm4260x_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + client->addr = ICM42607_ADDR; + return sensor_register_device(client, NULL, devid, &gsensor_icm4260x_ops); +} + +static void gsensor_icm4260x_remove(struct i2c_client *client) +{ + sensor_unregister_device(client, NULL, &gsensor_icm4260x_ops); +} + +static const struct i2c_device_id gsensor_icm4260x_id[] = { + {"icm42607_acc", ACCEL_ID_ICM4260X}, + {} +}; + +static struct i2c_driver gsensor_icm4260x_driver = { + .probe = gsensor_icm4260x_probe, + .remove = gsensor_icm4260x_remove, + .shutdown = sensor_shutdown, + .id_table = gsensor_icm4260x_id, + .driver = { + .name = "gsensor_icm4260x", +#ifdef CONFIG_PM + .pm = &sensor_pm_ops, +#endif + }, +}; + +module_i2c_driver(gsensor_icm4260x_driver); + +MODULE_AUTHOR("Wangqiang Guo "); +MODULE_DESCRIPTION("icm4260x_acc 3-Axis accelerometer driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/sensors/gyro/Kconfig b/drivers/input/sensors/gyro/Kconfig index 5b27389831ee..58b9b6dc0540 100644 --- a/drivers/input/sensors/gyro/Kconfig +++ b/drivers/input/sensors/gyro/Kconfig @@ -40,6 +40,12 @@ config GYRO_LSM330 config GYRO_ICM2060X tristate "gyroscope icm2060x_gyro" +config GYRO_ICM4260X + tristate "gyroscope icm4260x_gyro" + help + To have support for your specific gyro you will have to + select the proper drivers which depend on this option. + config GYRO_IAM20680 tristate "gyroscope iam20680_gyro" default n diff --git a/drivers/input/sensors/gyro/Makefile b/drivers/input/sensors/gyro/Makefile index 8c0a42734c65..d8c149baabc4 100644 --- a/drivers/input/sensors/gyro/Makefile +++ b/drivers/input/sensors/gyro/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_GYRO_MPU6500) += mpu6500_gyro.o obj-$(CONFIG_GYRO_MPU6880) += mpu6880_gyro.o obj-$(CONFIG_GYRO_LSM330) += lsm330_gyro.o obj-$(CONFIG_GYRO_ICM2060X) += icm2060x_gyro.o +obj-$(CONFIG_GYRO_ICM4260X) += icm4260x_gyro.o obj-$(CONFIG_GYRO_IAM20680) += iam20680_gyro.o diff --git a/drivers/input/sensors/gyro/icm4260x_gyro.c b/drivers/input/sensors/gyro/icm4260x_gyro.c new file mode 100644 index 000000000000..e66729d25116 --- /dev/null +++ b/drivers/input/sensors/gyro/icm4260x_gyro.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Rockchip Co.,Ltd. + * Author: Wangqiang Guo + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include + +static int sensor_active(struct i2c_client *client, int enable, int rate) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + int result = 0; + int status = 0; + + sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg); + + if (!enable) { + status = (0xff & ~BIT_GYRO_MODE_MASK); + sensor->ops->ctrl_data &= status; + } else { + status = BIT_GYRO_MODE_LNM; + sensor->ops->ctrl_data |= status; + sensor->ops->ctrl_data &= ~BIT_IDLE; + } + + result = sensor_write_reg(client, sensor->ops->ctrl_reg, + sensor->ops->ctrl_data); + if (result) { + dev_err(&client->dev, + "%s: fail to set pwr_mgmt0(%d)\n", __func__, result); + return result; + } + /* Gyroscope needs to be kept ON for a minimum of 45ms */ + usleep_range(45*1000, 45*1010); + + return result; +} + +static int sensor_init(struct i2c_client *client) +{ + int ret = 0; + u8 value; + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + /* + * init on icm42607_acc.c + */ + + /* set Full scale select for accelerometer UI interface output*/ + value = sensor_read_reg(client, ICM4260X_GYRO_CONFIG0); + value &= ~BIT_GYRO_FSR; + value |= GYRO_FS_SEL << SHIFT_GYRO_FS_SEL; + ret = sensor_write_reg(client, ICM4260X_GYRO_CONFIG0, value); + if (ret) + return ret; + + /* turn on accelerometer*/ + ret = sensor->ops->active(client, 0, sensor->pdata->poll_delay_ms); + if (ret) { + dev_err(&client->dev, + "%s: fail to active sensor(%d)\n", __func__, ret); + return ret; + } + + return ret; +} + +static int gyro_report_value(struct i2c_client *client, + struct sensor_axis *axis) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + + if (sensor->status_cur == SENSOR_ON) { + /* Report acceleration sensor information */ + input_report_abs(sensor->input_dev, ABS_RX, axis->x); + input_report_abs(sensor->input_dev, ABS_RY, axis->y); + input_report_abs(sensor->input_dev, ABS_RZ, axis->z); + input_sync(sensor->input_dev); + } + + return 0; +} + +static int sensor_report_value(struct i2c_client *client) +{ + struct sensor_private_data *sensor = + (struct sensor_private_data *) i2c_get_clientdata(client); + struct sensor_platform_data *pdata = sensor->pdata; + int ret = 0; + short x, y, z; + struct sensor_axis axis; + u8 buffer[6] = {0}; + + if (sensor->ops->read_len < 6) { + dev_err(&client->dev, "%s: length is error, len = %d\n", + __func__, sensor->ops->read_len); + return -EINVAL; + } + + *buffer = sensor->ops->read_reg; + ret = sensor_rx_data(client, buffer, sensor->ops->read_len); + if (ret < 0) { + dev_err(&client->dev, + "%s: read data failed, ret = %d\n", __func__, ret); + return ret; + } + + x = ((buffer[0] << 8) & 0xff00) + (buffer[1] & 0xFF); + y = ((buffer[2] << 8) & 0xff00) + (buffer[3] & 0xFF); + z = ((buffer[4] << 8) & 0xff00) + (buffer[5] & 0xFF); + + axis.x = (pdata->orientation[0]) * x + (pdata->orientation[1]) * y + + (pdata->orientation[2]) * z; + axis.y = (pdata->orientation[3]) * x + (pdata->orientation[4]) * y + + (pdata->orientation[5]) * z; + axis.z = (pdata->orientation[6]) * x + (pdata->orientation[7]) * y + + (pdata->orientation[8]) * z; + + gyro_report_value(client, &axis); + + mutex_lock(&(sensor->data_mutex)); + sensor->axis = axis; + mutex_unlock(&(sensor->data_mutex)); + + return ret; +} + +static struct sensor_operate gyro_icm4260x_ops = { + .name = "icm4260x_gyro", + .type = SENSOR_TYPE_GYROSCOPE, + .id_i2c = GYRO_ID_ICM4260X, + .read_reg = ICM4260X_GYRO_DATA_X0, + .read_len = 6, + .id_reg = SENSOR_UNKNOW_DATA, + .id_data = SENSOR_UNKNOW_DATA, + .precision = ICM4260X_PRECISION, + .ctrl_reg = ICM4260X_PWR_MGMT_0, + .int_status_reg = ICM4260X_INT_STATUS, + .range = {-32768, 32768}, + .trig = IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + .active = sensor_active, + .init = sensor_init, + .report = sensor_report_value, +}; + +/****************operate according to sensor chip:end************/ +static int gyro_icm4260x_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + client->addr = ICM42607_ADDR; + return sensor_register_device(client, NULL, devid, &gyro_icm4260x_ops); +} + +static void gyro_icm4260x_remove(struct i2c_client *client) +{ + sensor_unregister_device(client, NULL, &gyro_icm4260x_ops); +} + +static const struct i2c_device_id gyro_icm4260x_id[] = { + {"icm42607_gyro", GYRO_ID_ICM4260X}, + {} +}; + +static struct i2c_driver gyro_icm4260x_driver = { + .probe = gyro_icm4260x_probe, + .remove = gyro_icm4260x_remove, + .shutdown = sensor_shutdown, + .id_table = gyro_icm4260x_id, + .driver = { + .name = "gyro_icm4260x", +#ifdef CONFIG_PM + .pm = &sensor_pm_ops, +#endif + }, +}; + +static int __init gyro_icm4260x_init(void) +{ + return i2c_add_driver(&gyro_icm4260x_driver); +} + +static void __exit gyro_icm4260x_exit(void) +{ + i2c_del_driver(&gyro_icm4260x_driver); +} +/* must register after icm4260x_acc */ +device_initcall_sync(gyro_icm4260x_init); +module_exit(gyro_icm4260x_exit); + +MODULE_AUTHOR("Wangqiang Guo "); +MODULE_DESCRIPTION("icm4260x_gyro 3-Axis accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index 00eb807bea4a..ec3d2959475b 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -109,7 +109,7 @@ void gic_dist_config(void __iomem *base, int gic_irqs, amp_pri = 0; for (j = 0; j < 4; j++) { - if (rockchip_amp_check_amp_irq(i + j)) { + if (rockchip_amp_need_init_amp_irq(i + j)) { amp_pri |= rockchip_amp_get_irq_prio(i + j) << (j * 8); } else { diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 39ad43b55418..29414b90e9e2 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -272,7 +272,8 @@ static int gic_irq_set_irqchip_state(struct irq_data *d, u32 reg; #ifdef CONFIG_ROCKCHIP_AMP - if (rockchip_amp_check_amp_irq(gic_irq(d))) + if (which != IRQCHIP_STATE_PENDING && + rockchip_amp_check_amp_irq(gic_irq(d))) return -EINVAL; #endif switch (which) { @@ -528,7 +529,7 @@ static void gic_dist_init(struct gic_chip_data *gic) maskval = 0; for (j = 0; j < 4; j++) { - if (rockchip_amp_check_amp_irq(i + j)) { + if (rockchip_amp_need_init_amp_irq(i + j)) { maskval |= rockchip_amp_get_irq_cpumask(i + j) << (j * 8); } else { @@ -1362,6 +1363,9 @@ static int gic_init_bases(struct gic_chip_data *gic, goto error; } +#ifdef CONFIG_ROCKCHIP_AMP + rockchip_amp_get_gic_info(gic->gic_irqs, GIC_V2); +#endif gic_dist_init(gic); ret = gic_cpu_init(gic); if (ret) @@ -1569,10 +1573,6 @@ static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node) gic_enable_of_quirks(node, gic_quirks, gic); -#ifdef CONFIG_ROCKCHIP_AMP - rockchip_amp_get_gic_info(); -#endif - return 0; error: diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 9099ca01f9c3..dee3639506e8 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -190,6 +190,7 @@ obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_VIDEO_RK628) += rk628/ obj-$(CONFIG_VIDEO_RK_IRCUT) += rk_ircut.o +obj-$(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) += cam-tb-setup.o obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ obj-$(CONFIG_VIDEO_S5K3L6XX) += s5k3l6xx.o obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o diff --git a/drivers/media/i2c/cam-tb-setup.c b/drivers/media/i2c/cam-tb-setup.c new file mode 100644 index 000000000000..e300dc62731c --- /dev/null +++ b/drivers/media/i2c/cam-tb-setup.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2023 Rockchip Electronics Co., Ltd + +#include +#include +#include +#include "cam-tb-setup.h" + +static u32 rk_cam_w; +static u32 rk_cam_h; +static u32 rk_cam_hdr; +static u32 rk_cam_fps; + +static int __init rk_cam_w_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_w = (u32)val; + else + pr_err("get rk_cam_w fail\n"); + + return 0; +} + +u32 get_rk_cam_w(void) +{ + return rk_cam_w; +} +EXPORT_SYMBOL(get_rk_cam_w); + +static int __init rk_cam_h_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_h = (u32)val; + else + pr_err("get rk_cam_h fail\n"); + + return 0; +} + +u32 get_rk_cam_h(void) +{ + return rk_cam_h; +} +EXPORT_SYMBOL(get_rk_cam_h); + +static int __init rk_cam_hdr_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_hdr = (u32)val; + else + pr_err("get rk_cam_hdr fail\n"); + + return 0; +} + +u32 get_rk_cam_hdr(void) +{ + return rk_cam_hdr; +} +EXPORT_SYMBOL(get_rk_cam_hdr); + +static int __init __maybe_unused rk_cam_fps_setup(char *str) +{ + int ret = 0; + unsigned long val = 0; + + ret = kstrtoul(str, 0, &val); + if (!ret) + rk_cam_fps = (u32)val; + else + pr_err("get rk_cam_fps fail\n"); + + return 0; +} + +u32 get_rk_cam_fps(void) +{ + return rk_cam_fps; +} +EXPORT_SYMBOL(get_rk_cam_fps); + +__setup("rk_cam_w=", rk_cam_w_setup); +__setup("rk_cam_h=", rk_cam_h_setup); +__setup("rk_cam_hdr=", rk_cam_hdr_setup); +__setup("rk_cam_fps=", rk_cam_fps_setup); diff --git a/drivers/media/i2c/cam-tb-setup.h b/drivers/media/i2c/cam-tb-setup.h new file mode 100644 index 000000000000..61afc32c20b7 --- /dev/null +++ b/drivers/media/i2c/cam-tb-setup.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */ + +#ifndef CAM_TB_SETUP_H +#define CAM_TB_SETUP_H + +#include + +#ifdef CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP +u32 get_rk_cam_w(void); +u32 get_rk_cam_h(void); +u32 get_rk_cam_hdr(void); +u32 get_rk_cam_fps(void); +#else +static inline u32 get_rk_cam_w(void) +{ + return 0; +} +static inline u32 get_rk_cam_h(void) +{ + return 0; +} +static inline u32 get_rk_cam_hdr(void) +{ + return 0; +} +static inline u32 get_rk_cam_fps(void) +{ + return 0; +} +#endif + +#endif diff --git a/drivers/media/i2c/gc2093.c b/drivers/media/i2c/gc2093.c index a36f88af4f43..24ca78da31c5 100644 --- a/drivers/media/i2c/gc2093.c +++ b/drivers/media/i2c/gc2093.c @@ -33,6 +33,7 @@ #include #include #include "../platform/rockchip/isp/rkisp_tb_helper.h" +#include "cam-tb-setup.h" #define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) #define GC2093_NAME "gc2093" @@ -1607,68 +1608,6 @@ static const struct dev_pm_ops gc2093_pm_ops = { #ifdef CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP -static u32 rk_cam_hdr; -static u32 rk_cam_w; -static u32 rk_cam_h; -static u32 rk_cam_fps; - -static int __init __maybe_unused rk_cam_hdr_setup(char *str) -{ - int ret = 0; - unsigned long val = 0; - - ret = kstrtoul(str, 0, &val); - if (!ret) - rk_cam_hdr = (u32)val; - else - pr_err("get rk_cam_hdr fail\n"); - return 1; -} - -static int __init __maybe_unused rk_cam_w_setup(char *str) -{ - int ret = 0; - unsigned long val = 0; - - ret = kstrtoul(str, 0, &val); - if (!ret) - rk_cam_w = (u32)val; - else - pr_err("get rk_cam_w fail\n"); - return 1; -} - -static int __init __maybe_unused rk_cam_h_setup(char *str) -{ - int ret = 0; - unsigned long val = 0; - - ret = kstrtoul(str, 0, &val); - if (!ret) - rk_cam_h = (u32)val; - else - pr_err("get rk_cam_h fail\n"); - return 1; -} - -static int __init __maybe_unused rk_cam_fps_setup(char *str) -{ - int ret = 0; - unsigned long val = 0; - - ret = kstrtoul(str, 0, &val); - if (!ret) - rk_cam_fps = (u32)val; - else - pr_err("get rk_cam_fps fail\n"); - return 1; -} - -__setup("rk_cam_hdr=", rk_cam_hdr_setup); -__setup("rk_cam_w=", rk_cam_w_setup); -__setup("rk_cam_h=", rk_cam_h_setup); -__setup("rk_cam_fps=", rk_cam_fps_setup); - static void find_terminal_resolution(struct gc2093 *gc2093) { int i = 0; @@ -1677,6 +1616,10 @@ static void find_terminal_resolution(struct gc2093 *gc2093) u32 cur_fps = 0; u32 dst_fps = 0; u32 tmp_fps = 0; + u32 rk_cam_hdr = get_rk_cam_hdr(); + u32 rk_cam_w = get_rk_cam_w(); + u32 rk_cam_h = get_rk_cam_h(); + u32 rk_cam_fps = get_rk_cam_fps(); if (rk_cam_w == 0 || rk_cam_h == 0 || rk_cam_fps == 0) diff --git a/drivers/media/i2c/lt6911uxc.c b/drivers/media/i2c/lt6911uxc.c index cab313ca5a70..6584f395172a 100644 --- a/drivers/media/i2c/lt6911uxc.c +++ b/drivers/media/i2c/lt6911uxc.c @@ -6,6 +6,8 @@ * V0.0X01.0X00 first version. * V0.0X01.0X01 fix if plugin_gpio was not used. * V0.0X01.0X02 modify driver init level to late_initcall. + * V0.0X01.0X03 add 4K60 dual mipi support + * */ #include @@ -34,12 +36,16 @@ #include #include "lt6911uxc.h" -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x2) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x3) #define LT6911UXC_NAME "LT6911UXC" -#define LT6911UXC_LINK_FREQ_HIGH 400000000 -#define LT6911UXC_LINK_FREQ_LOW 200000000 -#define LT6911UXC_PIXEL_RATE 400000000 +#define LT6911UXC_LINK_FREQ_650M 650000000 +#define LT6911UXC_LINK_FREQ_400M 400000000 +#define LT6911UXC_LINK_FREQ_300M 300000000 +#define LT6911UXC_LINK_FREQ_200M 200000000 +#define LT6911UXC_LINK_FREQ_100M 100000000 +#define LT6911UXC_LINK_FREQ_60M 60000000 +#define LT6911UXC_PIXEL_RATE 600000000 #define I2C_MAX_XFER_SIZE 128 @@ -54,8 +60,12 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); static const s64 link_freq_menu_items[] = { - LT6911UXC_LINK_FREQ_HIGH, - LT6911UXC_LINK_FREQ_LOW, + LT6911UXC_LINK_FREQ_650M, + LT6911UXC_LINK_FREQ_400M, + LT6911UXC_LINK_FREQ_300M, + LT6911UXC_LINK_FREQ_200M, + LT6911UXC_LINK_FREQ_100M, + LT6911UXC_LINK_FREQ_60M, }; struct lt6911uxc { @@ -78,6 +88,7 @@ struct lt6911uxc { struct v4l2_dv_timings timings; struct v4l2_mbus_config_mipi_csi2 bus; struct v4l2_subdev sd; + struct rkmodule_multi_dev_info multi_dev_info; const char *len_name; const char *module_facing; const char *module_name; @@ -100,13 +111,25 @@ struct lt6911uxc_mode { u32 hts_def; u32 vts_def; u32 exp_def; + u32 mipi_freq_idx; +}; + +static struct rkmodule_csi_dphy_param rk3588_dcphy_param = { + .vendor = PHY_VENDOR_SAMSUNG, + .lp_vol_ref = 3, + .lp_hys_sw = {3, 0, 3, 0}, + .lp_escclk_pol_sel = {1, 1, 0, 0}, + .skew_data_cal_clk = {0, 0, 0, 0}, + .clk_hs_term_sel = 2, + .data_hs_term_sel = {2, 2, 2, 2}, + .reserved = {0}, }; static const struct v4l2_dv_timings_cap lt6911uxc_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 400000000, + V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 600000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | @@ -117,6 +140,16 @@ static const struct v4l2_dv_timings_cap lt6911uxc_timings_cap = { static const struct lt6911uxc_mode supported_modes[] = { { + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 600000, + }, + .hts_def = 4400, + .vts_def = 2250, + .mipi_freq_idx = 0, + }, { .width = 3840, .height = 2160, .max_fps = { @@ -125,6 +158,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 4400, .vts_def = 2250, + .mipi_freq_idx = 0, }, { .width = 1920, .height = 1080, @@ -134,6 +168,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 2200, .vts_def = 1125, + .mipi_freq_idx = 2, }, { .width = 1920, .height = 540, @@ -141,6 +176,7 @@ static const struct lt6911uxc_mode supported_modes[] = { .numerator = 10000, .denominator = 600000, }, + .mipi_freq_idx = 3, }, { .width = 1440, .height = 240, @@ -148,6 +184,7 @@ static const struct lt6911uxc_mode supported_modes[] = { .numerator = 10000, .denominator = 600000, }, + .mipi_freq_idx = 4, }, { .width = 1440, .height = 288, @@ -155,6 +192,7 @@ static const struct lt6911uxc_mode supported_modes[] = { .numerator = 10000, .denominator = 500000, }, + .mipi_freq_idx = 4, }, { .width = 1280, .height = 720, @@ -164,6 +202,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 1650, .vts_def = 750, + .mipi_freq_idx = 3, }, { .width = 720, .height = 576, @@ -173,6 +212,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 864, .vts_def = 625, + .mipi_freq_idx = 5, }, { .width = 720, .height = 480, @@ -182,6 +222,7 @@ static const struct lt6911uxc_mode supported_modes[] = { }, .hts_def = 858, .vts_def = 525, + .mipi_freq_idx = 5, }, }; @@ -390,6 +431,7 @@ static int lt6911uxc_get_detected_timings(struct v4l2_subdev *sd, u8 value, val_h, val_l; u32 fw_ver, mipi_byte_clk, mipi_bitrate; u8 fw_a, fw_b, fw_c, fw_d, lanes; + u8 video_fmt; int ret; memset(timings, 0, sizeof(struct v4l2_dv_timings)); @@ -421,13 +463,15 @@ static int lt6911uxc_get_detected_timings(struct v4l2_subdev *sd, i2c_rd8(sd, MIPI_LANES, &lanes); lt6911uxc->csi_lanes_in_use = lanes; + if (lt6911uxc->csi_lanes_in_use == 8) + v4l2_info(sd, "get 8 lane in use, set dual mipi mode\n"); i2c_wr8(sd, FM1_DET_CLK_SRC_SEL, AD_LMTX_WRITE_CLK); i2c_rd8(sd, FREQ_METER_H, &clk_h); i2c_rd8(sd, FREQ_METER_M, &clk_m); i2c_rd8(sd, FREQ_METER_L, &clk_l); mipi_byte_clk = (((clk_h & 0xf) << 16) | (clk_m << 8) | clk_l); mipi_bitrate = mipi_byte_clk * 8 / 1000; - v4l2_info(sd, "MIPI Byte clk: %dKHz, MIPI bitrate: %dMbps, lanes:%d\n", + v4l2_info(sd, "MIPI Byte clk: %uKHz, MIPI bitrate: %uMbps, lanes:%d\n", mipi_byte_clk, mipi_bitrate, lanes); i2c_rd8(sd, HTOTAL_H, &val_h); @@ -457,8 +501,16 @@ static int lt6911uxc_get_detected_timings(struct v4l2_subdev *sd, hbp = ((val_h << 8) | val_l) * 2; i2c_rd8(sd, VBP, &value); vbp = value; + i2c_rd8(sd, COLOR_FMT_STATUS, &video_fmt); + video_fmt = (video_fmt & GENMASK(6, 5)) >> 5; lt6911uxc_i2c_disable(sd); + if (video_fmt == 0x3) { + lt6911uxc->nosignal = true; + v4l2_err(sd, "%s ERROR: HDMI input YUV420, don't support YUV420!\n", __func__); + return -EINVAL; + } + if (!lt6911uxc_rcv_supported_res(sd, hact, vact)) { lt6911uxc->nosignal = true; v4l2_err(sd, "%s: rcv err res, return no signal!\n", __func__); @@ -694,7 +746,6 @@ static int lt6911uxc_s_dv_timings(struct v4l2_subdev *sd, } lt6911uxc->timings = *timings; - enable_stream(sd, false); return 0; @@ -818,11 +869,50 @@ static int lt6911uxc_enum_frame_interval(struct v4l2_subdev *sd, return 0; } +static int lt6911uxc_get_reso_dist(const struct lt6911uxc_mode *mode, + struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + u32 cur_fps, dist_fps; + + cur_fps = fps_calc(bt); + dist_fps = DIV_ROUND_CLOSEST(mode->max_fps.denominator, mode->max_fps.numerator); + + return abs(mode->width - bt->width) + + abs(mode->height - bt->height) + abs(dist_fps - cur_fps); +} + +static const struct lt6911uxc_mode * +lt6911uxc_find_best_fit(struct lt6911uxc *lt6911uxc) +{ + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = lt6911uxc_get_reso_dist(&supported_modes[i], <6911uxc->timings); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + dev_info(<6911uxc->i2c_client->dev, + "find current mode: support_mode[%d], %dx%d@%dfps\n", + cur_best_fit, supported_modes[cur_best_fit].width, + supported_modes[cur_best_fit].height, + DIV_ROUND_CLOSEST(supported_modes[cur_best_fit].max_fps.denominator, + supported_modes[cur_best_fit].max_fps.numerator)); + + return &supported_modes[cur_best_fit]; +} + static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct lt6911uxc *lt6911uxc = to_state(sd); + const struct lt6911uxc_mode *mode; mutex_lock(<6911uxc->confctl_mutex); format->format.code = lt6911uxc->mbus_fmt_code; @@ -832,6 +922,14 @@ static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, lt6911uxc->timings.bt.interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; format->format.colorspace = V4L2_COLORSPACE_SRGB; + + mode = lt6911uxc_find_best_fit(lt6911uxc); + lt6911uxc->cur_mode = mode; + __v4l2_ctrl_s_ctrl_int64(lt6911uxc->pixel_rate, + LT6911UXC_PIXEL_RATE); + __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, + mode->mipi_freq_idx); + mutex_unlock(<6911uxc->confctl_mutex); v4l2_dbg(1, debug, sd, "%s: fmt code:%d, w:%d, h:%d, field mode:%s\n", @@ -841,40 +939,12 @@ static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, return 0; } -static int lt6911uxc_get_reso_dist(const struct lt6911uxc_mode *mode, - struct v4l2_mbus_framefmt *framefmt) -{ - return abs(mode->width - framefmt->width) + - abs(mode->height - framefmt->height); -} - -static const struct lt6911uxc_mode * -lt6911uxc_find_best_fit(struct v4l2_subdev_format *fmt) -{ - struct v4l2_mbus_framefmt *framefmt = &fmt->format; - int dist; - int cur_best_fit = 0; - int cur_best_fit_dist = -1; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { - dist = lt6911uxc_get_reso_dist(&supported_modes[i], framefmt); - if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { - cur_best_fit_dist = dist; - cur_best_fit = i; - } - } - - return &supported_modes[cur_best_fit]; -} - static int lt6911uxc_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct lt6911uxc *lt6911uxc = to_state(sd); const struct lt6911uxc_mode *mode; - int index; /* is overwritten by get_fmt */ u32 code = format->format.code; @@ -897,20 +967,10 @@ static int lt6911uxc_set_fmt(struct v4l2_subdev *sd, return 0; lt6911uxc->mbus_fmt_code = format->format.code; - mode = lt6911uxc_find_best_fit(format); + mode = lt6911uxc_find_best_fit(lt6911uxc); lt6911uxc->cur_mode = mode; enable_stream(sd, false); - if (((mode->width == 720) && (mode->height == 576)) || - ((mode->width == 720) && (mode->height == 480))) - index = 1; - else - index = 0; - - __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, index); - v4l2_dbg(1, debug, sd, "%s res wxh:%dx%d, link freq:%llu", __func__, - mode->width, mode->height, link_freq_menu_items[index]); - return 0; } @@ -939,7 +999,10 @@ static void lt6911uxc_get_module_inf(struct lt6911uxc *lt6911uxc, static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct lt6911uxc *lt6911uxc = to_state(sd); + struct device *dev = <6911uxc->i2c_client->dev; long ret = 0; + struct rkmodule_csi_dphy_param *dphy_param; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -948,6 +1011,29 @@ static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) case RKMODULE_GET_HDMI_MODE: *(int *)arg = RKMODULE_HDMIIN_MODE; break; + case RKMODULE_SET_CSI_DPHY_PARAM: + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + if (dphy_param->vendor == PHY_VENDOR_SAMSUNG) + rk3588_dcphy_param = *dphy_param; + dev_dbg(<6911uxc->i2c_client->dev, + "sensor set dphy param\n"); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + *dphy_param = rk3588_dcphy_param; + dev_dbg(<6911uxc->i2c_client->dev, + "sensor get dphy param\n"); + break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = (struct rkmodule_capture_info *)arg; + if (lt6911uxc->csi_lanes_in_use == 8) { + dev_info(dev, "8 lanes in use, set dual mipi mode\n"); + capture_info->mode = RKMODULE_MULTI_DEV_COMBINE_ONE; + capture_info->multi_dev = lt6911uxc->multi_dev_info; + } else { + capture_info->mode = 0; + } + break; default: ret = -ENOIOCTLCMD; break; @@ -964,6 +1050,8 @@ static long lt6911uxc_compat_ioctl32(struct v4l2_subdev *sd, struct rkmodule_inf *inf; long ret; int *seq; + struct rkmodule_csi_dphy_param *dphy_param; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -996,6 +1084,50 @@ static long lt6911uxc_compat_ioctl32(struct v4l2_subdev *sd, } kfree(seq); break; + case RKMODULE_SET_CSI_DPHY_PARAM: + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); + if (!dphy_param) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(dphy_param, up, sizeof(*dphy_param)); + if (!ret) + ret = lt6911uxc_ioctl(sd, cmd, dphy_param); + else + ret = -EFAULT; + kfree(dphy_param); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); + if (!dphy_param) { + ret = -ENOMEM; + return ret; + } + + ret = lt6911uxc_ioctl(sd, cmd, dphy_param); + if (!ret) { + ret = copy_to_user(up, dphy_param, sizeof(*dphy_param)); + if (ret) + ret = -EFAULT; + } + kfree(dphy_param); + break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = kzalloc(sizeof(*capture_info), GFP_KERNEL); + if (!capture_info) { + ret = -ENOMEM; + return ret; + } + + ret = lt6911uxc_ioctl(sd, cmd, capture_info); + if (!ret) { + ret = copy_to_user(up, capture_info, sizeof(*capture_info)); + if (ret) + ret = -EFAULT; + } + kfree(capture_info); + break; default: ret = -ENOIOCTLCMD; break; @@ -1075,9 +1207,11 @@ static void lt6911uxc_reset(struct lt6911uxc *lt6911uxc) static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) { + const struct lt6911uxc_mode *mode; struct v4l2_subdev *sd; int ret; + mode = lt6911uxc->cur_mode; sd = <6911uxc->sd; ret = v4l2_ctrl_handler_init(<6911uxc->hdl, 5); if (ret) @@ -1087,8 +1221,9 @@ static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) V4L2_CID_LINK_FREQ, ARRAY_SIZE(link_freq_menu_items) - 1, 0, link_freq_menu_items); - v4l2_ctrl_new_std(<6911uxc->hdl, NULL, V4L2_CID_PIXEL_RATE, - 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); + lt6911uxc->pixel_rate = v4l2_ctrl_new_std(<6911uxc->hdl, NULL, + V4L2_CID_PIXEL_RATE, + 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); lt6911uxc->detect_tx_5v_ctrl = v4l2_ctrl_new_std(<6911uxc->hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, @@ -1107,6 +1242,9 @@ static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) return ret; } + __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, mode->mipi_freq_idx); + __v4l2_ctrl_s_ctrl_int64(lt6911uxc->pixel_rate, LT6911UXC_PIXEL_RATE); + if (lt6911uxc_update_controls(sd)) { ret = -ENODEV; v4l2_err(sd, "update v4l2 ctrls failed! ret:%d\n", ret); @@ -1291,6 +1429,36 @@ static struct attribute *lt6911_attrs[] = { }; ATTRIBUTE_GROUPS(lt6911); +static int lt6911uxc_get_multi_dev_info(struct lt6911uxc *lt6911uxc) +{ + struct device *dev = <6911uxc->i2c_client->dev; + struct device_node *node = dev->of_node; + struct device_node *multi_info_np; + + multi_info_np = of_get_child_by_name(node, "multi-dev-info"); + if (!multi_info_np) { + dev_info(dev, "failed to get multi dev info\n"); + return -EINVAL; + } + + of_property_read_u32(multi_info_np, "dev-idx-l", + <6911uxc->multi_dev_info.dev_idx[0]); + of_property_read_u32(multi_info_np, "dev-idx-r", + <6911uxc->multi_dev_info.dev_idx[1]); + of_property_read_u32(multi_info_np, "combine-idx", + <6911uxc->multi_dev_info.combine_idx[0]); + of_property_read_u32(multi_info_np, "pixel-offset", + <6911uxc->multi_dev_info.pixel_offset); + of_property_read_u32(multi_info_np, "dev-num", + <6911uxc->multi_dev_info.dev_num); + dev_info(dev, + "multi dev left: mipi%d, multi dev right: mipi%d, combile mipi%d, dev num: %d\n", + lt6911uxc->multi_dev_info.dev_idx[0], lt6911uxc->multi_dev_info.dev_idx[1], + lt6911uxc->multi_dev_info.combine_idx[0], lt6911uxc->multi_dev_info.dev_num); + + return 0; +} + static int lt6911uxc_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1323,6 +1491,10 @@ static int lt6911uxc_probe(struct i2c_client *client, return err; } + err = lt6911uxc_get_multi_dev_info(lt6911uxc); + if (err) + v4l2_info(sd, "get multi dev info failed, not use dual mipi mode\n"); + err = lt6911uxc_check_chip_id(lt6911uxc); if (err < 0) return err; @@ -1477,9 +1649,10 @@ static void __exit lt6911uxc_driver_exit(void) i2c_del_driver(<6911uxc_driver); } -late_initcall(lt6911uxc_driver_init); +device_initcall_sync(lt6911uxc_driver_init); module_exit(lt6911uxc_driver_exit); MODULE_DESCRIPTION("Lontium LT6911UXC HDMI to MIPI CSI-2 bridge driver"); MODULE_AUTHOR("Dingxian Wen "); +MODULE_AUTHOR("Jianwei Fan "); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/lt6911uxc.h b/drivers/media/i2c/lt6911uxc.h index 45cb29764879..29c9217172ba 100644 --- a/drivers/media/i2c/lt6911uxc.h +++ b/drivers/media/i2c/lt6911uxc.h @@ -65,4 +65,9 @@ #define AUDIO_SAMPLE_RATAE_H 0xb0aa #define AUDIO_SAMPLE_RATAE_L 0xb0ab +#define COLOR_FMT_STATUS 0xb092 +#define STREAM_CTRL 0x811d +#define STREAM_ENABLE 0xFB +#define STREAM_DISABLE 0xBB + #endif diff --git a/drivers/media/i2c/maxim4c/Makefile b/drivers/media/i2c/maxim4c/Makefile index f1ee630c6c35..665a8669d041 100644 --- a/drivers/media/i2c/maxim4c/Makefile +++ b/drivers/media/i2c/maxim4c/Makefile @@ -7,7 +7,8 @@ maxim4c-objs += maxim4c_i2c.o \ maxim4c_remote.o \ maxim4c_pattern.o \ maxim4c_v4l2.o \ - maxim4c_drv.o + maxim4c_drv.o \ + maxim4c_debugfs.o obj-$(CONFIG_MAXIM4C_SER_MAX9295) += remote_max9295.o obj-$(CONFIG_MAXIM4C_SER_MAX96715) += remote_max96715.o diff --git a/drivers/media/i2c/maxim4c/maxim4c_api.h b/drivers/media/i2c/maxim4c/maxim4c_api.h index 6dd3bbca17d7..25f261c84a44 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_api.h +++ b/drivers/media/i2c/maxim4c/maxim4c_api.h @@ -98,4 +98,7 @@ int maxim4c_pattern_support_mode_init(maxim4c_t *maxim4c); int maxim4c_pattern_data_init(maxim4c_t *maxim4c); int maxim4c_pattern_enable(maxim4c_t *maxim4c, bool enable); +int maxim4c_dbgfs_init(maxim4c_t *maxim4c); +void maxim4c_dbgfs_deinit(maxim4c_t *maxim4c); + #endif /* __MAXIM4C_API_H__ */ diff --git a/drivers/media/i2c/maxim4c/maxim4c_debugfs.c b/drivers/media/i2c/maxim4c/maxim4c_debugfs.c new file mode 100644 index 000000000000..75dddd7114e8 --- /dev/null +++ b/drivers/media/i2c/maxim4c/maxim4c_debugfs.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim Quad GMSL Deserializer debugfs helper functions + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * Author: Cai Wenzhong + * + */ +#include +#include + +#include "maxim4c_api.h" + +int maxim4c_dbgfs_init(maxim4c_t *maxim4c) +{ + struct dentry *entry; + + entry = debugfs_create_dir("maxim4c", NULL); + + debugfs_create_u8("timing_override_en", 0600, entry, + &maxim4c->mipi_txphy.timing_override_en); + + debugfs_create_u8("t_hs_przero", 0600, entry, + &maxim4c->mipi_txphy.timing.t_hs_przero); + debugfs_create_u8("t_hs_prep", 0600, entry, + &maxim4c->mipi_txphy.timing.t_hs_prep); + debugfs_create_u8("t_clk_trail", 0600, entry, + &maxim4c->mipi_txphy.timing.t_clk_trail); + debugfs_create_u8("t_clk_przero", 0600, entry, + &maxim4c->mipi_txphy.timing.t_clk_przero); + debugfs_create_u8("t_lpx", 0600, entry, &maxim4c->mipi_txphy.timing.t_lpx); + debugfs_create_u8("t_hs_trail", 0600, entry, + &maxim4c->mipi_txphy.timing.t_hs_trail); + + debugfs_create_u8("t_clk_prep", 0600, entry, + &maxim4c->mipi_txphy.timing.t_clk_prep); + debugfs_create_u8("t_lpxesc", 0600, entry, + &maxim4c->mipi_txphy.timing.t_lpxesc); + + debugfs_create_u8("csi2_t_pre", 0600, entry, + &maxim4c->mipi_txphy.timing.csi2_t_pre); + debugfs_create_u8("csi2_t_post", 0600, entry, + &maxim4c->mipi_txphy.timing.csi2_t_post); + debugfs_create_u8("csi2_tx_gap", 0600, entry, + &maxim4c->mipi_txphy.timing.csi2_tx_gap); + debugfs_create_u32("csi2_twakeup", 0600, entry, + &maxim4c->mipi_txphy.timing.csi2_twakeup); + + maxim4c->dbgfs_root = entry; + + return 0; +} +EXPORT_SYMBOL(maxim4c_dbgfs_init); + +void maxim4c_dbgfs_deinit(maxim4c_t *maxim4c) +{ + debugfs_remove_recursive(maxim4c->dbgfs_root); +} +EXPORT_SYMBOL(maxim4c_dbgfs_deinit); diff --git a/drivers/media/i2c/maxim4c/maxim4c_drv.c b/drivers/media/i2c/maxim4c/maxim4c_drv.c index 60d8955daac4..16d11afb4828 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_drv.c +++ b/drivers/media/i2c/maxim4c/maxim4c_drv.c @@ -29,6 +29,11 @@ * V2.03.00 * 1. remote device add the maxim4c prefix to driver name. * + * V2.04.02 + * 1. Add regulator supplier dependencies. + * 2. Add config ssc-ratio property + * 3. Add debugfs entry to change MIPI timing + * */ #include #include @@ -58,10 +63,15 @@ #include "maxim4c_api.h" -#define DRIVER_VERSION KERNEL_VERSION(2, 0x03, 0x00) +#define DRIVER_VERSION KERNEL_VERSION(2, 0x04, 0x02) #define MAXIM4C_XVCLK_FREQ 25000000 +static const char *const maxim4c_supply_names[MAXIM4C_NUM_SUPPLIES] = { + "vcc1v2", + "vcc1v8", +}; + static int maxim4c_check_local_chipid(maxim4c_t *maxim4c) { struct i2c_client *client = maxim4c->client; @@ -302,6 +312,13 @@ static inline u32 maxim4c_cal_delay(u32 cycles) static int maxim4c_local_device_power_on(maxim4c_t *maxim4c) { struct device *dev = &maxim4c->client->dev; + int ret; + + ret = regulator_bulk_enable(MAXIM4C_NUM_SUPPLIES, maxim4c->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + return -EINVAL; + } if (!IS_ERR(maxim4c->pwdn_gpio)) { dev_info(dev, "local device pwdn gpio on\n"); @@ -317,24 +334,30 @@ static int maxim4c_local_device_power_on(maxim4c_t *maxim4c) static void maxim4c_local_device_power_off(maxim4c_t *maxim4c) { struct device *dev = &maxim4c->client->dev; + int ret; if (!IS_ERR(maxim4c->pwdn_gpio)) { dev_info(dev, "local device pwdn gpio off\n"); gpiod_set_value_cansleep(maxim4c->pwdn_gpio, 0); } + + ret = regulator_bulk_disable(MAXIM4C_NUM_SUPPLIES, maxim4c->supplies); + if (ret < 0) { + dev_warn(dev, "Failed to disable regulators\n"); + } } static int maxim4c_remote_device_power_on(maxim4c_t *maxim4c) { struct device *dev = &maxim4c->client->dev; + int ret; - // remote PoC enable - if (!IS_ERR(maxim4c->pocen_gpio)) { - dev_info(dev, "remote device pocen gpio on\n"); - - gpiod_set_value_cansleep(maxim4c->pocen_gpio, 1); - usleep_range(5000, 10000); + dev_dbg(dev, "Turn PoC on\n"); + ret = regulator_enable(maxim4c->poc_regulator); + if (ret < 0) { + dev_err(dev, "Unable to turn PoC on\n"); + return ret; } return 0; @@ -343,13 +366,12 @@ static int maxim4c_remote_device_power_on(maxim4c_t *maxim4c) static int maxim4c_remote_device_power_off(maxim4c_t *maxim4c) { struct device *dev = &maxim4c->client->dev; + int ret; - // remote PoC enable - if (!IS_ERR(maxim4c->pocen_gpio)) { - dev_info(dev, "remote device pocen gpio off\n"); - - gpiod_set_value_cansleep(maxim4c->pocen_gpio, 0); - } + dev_dbg(dev, "Turn PoC off\n"); + ret = regulator_disable(maxim4c->poc_regulator); + if (ret < 0) + dev_warn(dev, "Unable to turn PoC off\n"); return 0; } @@ -581,6 +603,7 @@ static int maxim4c_probe(struct i2c_client *client, maxim4c_t *maxim4c = NULL; u32 chip_id; int ret = 0; + unsigned int i; dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16, (DRIVER_VERSION & 0xff00) >> 8, DRIVER_VERSION & 0x00ff); @@ -621,14 +644,37 @@ static int maxim4c_probe(struct i2c_client *client, if (IS_ERR(maxim4c->pwdn_gpio)) dev_warn(dev, "Failed to get pwdn-gpios, maybe no use\n"); - maxim4c->pocen_gpio = devm_gpiod_get(dev, "pocen", GPIOD_OUT_LOW); - if (IS_ERR(maxim4c->pocen_gpio)) - dev_warn(dev, "Failed to get pocen-gpios\n"); - maxim4c->lock_gpio = devm_gpiod_get(dev, "lock", GPIOD_IN); if (IS_ERR(maxim4c->lock_gpio)) dev_warn(dev, "Failed to get lock-gpios\n"); + for (i = 0; i < MAXIM4C_NUM_SUPPLIES; i++) + maxim4c->supplies[i].supply = maxim4c_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, MAXIM4C_NUM_SUPPLIES, + maxim4c->supplies); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Unable to get supply regulators\n"); + else + dev_warn(dev, "Get PoC regulator deferred\n"); + return ret; + } + + maxim4c->poc_regulator = devm_regulator_get(dev, "poc"); + if (IS_ERR(maxim4c->poc_regulator)) { + if (PTR_ERR(maxim4c->poc_regulator) != -EPROBE_DEFER) + dev_err(dev, "Unable to get PoC regulator (%ld)\n", + PTR_ERR(maxim4c->poc_regulator)); + else + dev_err(dev, "Get PoC regulator deferred\n"); + + ret = PTR_ERR(maxim4c->poc_regulator); +#if !MAXIM4C_TEST_PATTERN + return ret; +#endif + } + mutex_init(&maxim4c->mutex); ret = maxim4c_local_device_power_on(maxim4c); @@ -668,15 +714,19 @@ static int maxim4c_probe(struct i2c_client *client, maxim4c_module_data_init(maxim4c); maxim4c_module_parse_dt(maxim4c); + ret = maxim4c_dbgfs_init(maxim4c); + if (ret) + goto err_subdev_deinit; + #if (MAXIM4C_LOCAL_DES_ON_OFF_EN == 0) ret = maxim4c_module_hw_init(maxim4c); if (ret) - goto err_subdev_deinit; + goto err_dbgfs_deinit; #endif /* MAXIM4C_LOCAL_DES_ON_OFF_EN */ ret = maxim4c_remote_mfd_add_devices(maxim4c); if (ret) - goto err_subdev_deinit; + goto err_dbgfs_deinit; maxim4c_lock_irq_init(maxim4c); maxim4c_lock_state_work_init(maxim4c); @@ -687,6 +737,8 @@ static int maxim4c_probe(struct i2c_client *client, return 0; +err_dbgfs_deinit: + maxim4c_dbgfs_deinit(maxim4c); err_subdev_deinit: maxim4c_v4l2_subdev_deinit(maxim4c); err_power_off: @@ -703,6 +755,8 @@ static int maxim4c_remove(struct i2c_client *client) maxim4c_lock_state_work_deinit(maxim4c); + maxim4c_dbgfs_deinit(maxim4c); + maxim4c_v4l2_subdev_deinit(maxim4c); mutex_destroy(&maxim4c->mutex); diff --git a/drivers/media/i2c/maxim4c/maxim4c_drv.h b/drivers/media/i2c/maxim4c/maxim4c_drv.h index 444f20f5d3e8..d4542e15eeb2 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_drv.h +++ b/drivers/media/i2c/maxim4c/maxim4c_drv.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,8 @@ #define MAX96712_CHIP_ID 0xA0 #define MAX96722_CHIP_ID 0xA1 +#define MAXIM4C_NUM_SUPPLIES 2 + enum { MAXIM4C_HOT_PLUG_OUT = 0, MAXIM4C_HOT_PLUG_IN, @@ -57,8 +60,9 @@ typedef struct maxim4c { struct i2c_client *client; struct clk *xvclk; struct gpio_desc *pwdn_gpio; - struct gpio_desc *pocen_gpio; struct gpio_desc *lock_gpio; + struct regulator_bulk_data supplies[MAXIM4C_NUM_SUPPLIES]; + struct regulator *poc_regulator; struct mutex mutex; @@ -104,6 +108,8 @@ typedef struct maxim4c { struct mfd_cell remote_mfd_devs[MAXIM4C_LINK_ID_MAX]; maxim4c_remote_t *remote_device[MAXIM4C_LINK_ID_MAX]; + + struct dentry *dbgfs_root; } maxim4c_t; #endif /* __MAXIM4C_DRV_H__ */ diff --git a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c index 5734902e59f8..8776401149fb 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c +++ b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c @@ -11,6 +11,82 @@ #include "maxim4c_api.h" +static int maxim4c_txphy_init_timing(maxim4c_t *maxim4c) +{ + struct i2c_client *client = maxim4c->client; + int ret = 0; + u16 reg_addr = 0; + u8 reg_mask; + u8 timing; + u8 phy_idx = 0; + + if (!maxim4c->mipi_txphy.timing_override_en) + return 0; + + timing = ((maxim4c->mipi_txphy.timing.t_hs_przero & 0x3) << 6 | + (maxim4c->mipi_txphy.timing.t_hs_prep & 0x3) << 4 | + (maxim4c->mipi_txphy.timing.t_clk_trail & 0x3) << 2 | + (maxim4c->mipi_txphy.timing.t_clk_przero & 0x3) << 0); + + ret |= maxim4c_i2c_write_byte(client, 0x08A1, + MAXIM4C_I2C_REG_ADDR_16BITS, timing); + + reg_mask = 0x0F; + timing = ((maxim4c->mipi_txphy.timing.t_lpx & 0x3) << 2 | + (maxim4c->mipi_txphy.timing.t_hs_trail & 0x3) << 0); + + ret |= maxim4c_i2c_update_byte( + client, 0x08A2, MAXIM4C_I2C_REG_ADDR_16BITS, reg_mask, timing); + + reg_mask = (0x3 << 6); + timing = (maxim4c->mipi_txphy.timing.t_lpxesc & 0x3) << 6; + ret |= maxim4c_i2c_update_byte( + client, 0x08A5, MAXIM4C_I2C_REG_ADDR_16BITS, reg_mask, timing); + + reg_mask = (0x7 << 5); + timing = (maxim4c->mipi_txphy.timing.t_lpxesc & 0x7) << 5; + ret |= maxim4c_i2c_update_byte( + client, 0x08A8, MAXIM4C_I2C_REG_ADDR_16BITS, reg_mask, timing); + + for (phy_idx = 0; phy_idx < MAXIM4C_TXPHY_ID_MAX; phy_idx++) { + reg_mask = 0xFF; + reg_addr = 0x0905 + 0x40 * phy_idx; + timing = maxim4c->mipi_txphy.timing.csi2_t_pre; + ret |= maxim4c_i2c_update_byte(client, reg_addr, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + + reg_addr = 0x0906 + 0x40 * phy_idx; + timing = maxim4c->mipi_txphy.timing.csi2_t_post; + ret |= maxim4c_i2c_update_byte(client, reg_addr, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + + reg_addr = 0x0907 + 0x40 * phy_idx; + timing = maxim4c->mipi_txphy.timing.csi2_tx_gap; + ret |= maxim4c_i2c_update_byte(client, reg_addr, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + + reg_addr = 0x0908 + 0x40 * phy_idx; + timing = maxim4c->mipi_txphy.timing.csi2_twakeup & 0xFF; + ret |= maxim4c_i2c_update_byte(client, reg_addr, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + timing = (maxim4c->mipi_txphy.timing.csi2_twakeup >> 8) & 0xFF; + ret |= maxim4c_i2c_update_byte(client, reg_addr + 1, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + reg_mask = 0x7; + timing = (maxim4c->mipi_txphy.timing.csi2_twakeup >> 16) & 0x7; + ret |= maxim4c_i2c_update_byte(client, reg_addr + 2, + MAXIM4C_I2C_REG_ADDR_16BITS, + reg_mask, timing); + } + + return ret; +} + static int maxim4c_txphy_auto_init_deskew(maxim4c_t *maxim4c) { struct i2c_client *client = maxim4c->client; @@ -182,6 +258,11 @@ int maxim4c_dphy_dpll_predef_set(maxim4c_t *maxim4c, s64 link_freq_hz) reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS, 0xf4); + reg_addr = 0x1C03 + 0x100 * phy_idx; + ret |= maxim4c_i2c_update_byte(client, reg_addr, + MAXIM4C_I2C_REG_ADDR_16BITS, + 0x07, phy_cfg->ssc_ratio); + // Set dpll data rate reg_addr = 0x0415 + 0x03 * phy_idx; ret |= maxim4c_i2c_update_byte(client, @@ -349,6 +430,12 @@ static int maxim4c_mipi_txphy_config_parse_dt(struct device *dev, phy_cfg->clock_mode = value; } + ret = of_property_read_u32(node, "ssc-ratio", &value); + if (ret == 0) { + dev_info(dev, "ssc-ratio property: %d", value); + phy_cfg->ssc_ratio = value; + } + sub_idx++; } } @@ -510,6 +597,9 @@ int maxim4c_mipi_txphy_hw_init(maxim4c_t *maxim4c) // mipi txphy auto init deskew ret |= maxim4c_txphy_auto_init_deskew(maxim4c); + // mipi txphy timing init + ret |= maxim4c_txphy_init_timing(maxim4c); + if (ret) { dev_err(dev, "%s: txphy hw init error\n", __func__); return ret; @@ -529,6 +619,11 @@ void maxim4c_mipi_txphy_data_init(maxim4c_t *maxim4c) mipi_txphy->force_clock_out_en = 1; mipi_txphy->force_clk0_en = 0; mipi_txphy->force_clk3_en = 0; + mipi_txphy->timing.t_lpx = 1; + mipi_txphy->timing.csi2_t_pre = 0x71; + mipi_txphy->timing.csi2_t_post = 0x19; + mipi_txphy->timing.csi2_tx_gap = 0x1C; + mipi_txphy->timing.csi2_twakeup = 0x100; for (i = 0; i < MAXIM4C_TXPHY_ID_MAX; i++) { phy_cfg = &mipi_txphy->phy_cfg[i]; @@ -541,6 +636,7 @@ void maxim4c_mipi_txphy_data_init(maxim4c_t *maxim4c) phy_cfg->vc_ext_en = 0; phy_cfg->clock_master = 0; phy_cfg->clock_mode = MAXIM4C_TXPHY_DPLL_PREDEF; + phy_cfg->ssc_ratio = 0; } } EXPORT_SYMBOL(maxim4c_mipi_txphy_data_init); diff --git a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h index 759488d71625..29416be78a82 100644 --- a/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h +++ b/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h @@ -53,6 +53,38 @@ struct maxim4c_txphy_cfg { u8 vc_ext_en; u8 clock_master; u8 clock_mode; + u8 ssc_ratio; +}; + +struct maxim4c_txphy_timing { + /* 0x8A1 */ + u8 t_hs_przero; + u8 t_hs_prep; + u8 t_clk_trail; + u8 t_clk_przero; + + /* 0x8A2 */ + u8 t_lpx; + u8 t_hs_trail; + + /* 0x8A5 */ + u8 t_clk_prep; + + /* 0x8A8 */ + u8 t_lpxesc; + + /* 0x8AE */ + u8 t_t3_post; + u8 t_t3_prep; + + /* 0x905 */ + u8 csi2_t_pre; + /* 0x906 */ + u8 csi2_t_post; + /* 0x907 */ + u8 csi2_tx_gap; + /* 0x908,0x909,0x90A */ + u32 csi2_twakeup; }; typedef struct maxim4c_mipi_txphy { @@ -61,6 +93,9 @@ typedef struct maxim4c_mipi_txphy { u8 force_clk0_en; /* DPHY0 enabled as clock */ u8 force_clk3_en; /* DPHY3 enabled as clock */ + u8 timing_override_en; + struct maxim4c_txphy_timing timing; + struct maxim4c_txphy_cfg phy_cfg[MAXIM4C_TXPHY_ID_MAX]; } maxim4c_mipi_txphy_t; diff --git a/drivers/media/i2c/sc401ai.c b/drivers/media/i2c/sc401ai.c index ba4a36c1bb4a..2a3cfbe8812e 100644 --- a/drivers/media/i2c/sc401ai.c +++ b/drivers/media/i2c/sc401ai.c @@ -9,6 +9,7 @@ * V0.0X01.0X03 fix gain range. * V0.0X01.0X04 add enum_frame_interval function. * V0.0X01.0X05 add quick stream on/off + * V0.0X01.0X06 support thunder boot function. */ #include @@ -31,8 +32,9 @@ #include #include #include +#include "../platform/rockchip/isp/rkisp_tb_helper.h" -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x05) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x06) #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN @@ -173,6 +175,8 @@ struct sc401ai { const char *module_name; const char *len_name; u32 cur_vts; + bool is_thunderboot; + bool is_first_streamoff; struct preisp_hdrae_exp_s init_hdrae_exp; }; @@ -447,6 +451,8 @@ static const s64 link_freq_menu_items[] = { SC401AI_LINK_FREQ_630, }; +static int __sc401ai_power_on(struct sc401ai *sc401ai); + /* Write registers up to 4 at a time */ static int sc401ai_write_reg(struct i2c_client *client, u16 reg, u32 len, u32 val) @@ -624,6 +630,11 @@ static int sc401ai_set_gain_reg(struct sc401ai *sc401ai, u32 gain) DIG_Fine_gain_reg = abs(800 * gain / (Dcg_gainx100 * Coarse_gain * DIG_gain) / ANA_Fine_gainx64); + if (sc401ai->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + sc401ai->is_thunderboot = false; + __sc401ai_power_on(sc401ai); + } + ret = sc401ai_write_reg(sc401ai->client, SC401AI_REG_DIG_GAIN, SC401AI_REG_VALUE_08BIT, @@ -978,23 +989,31 @@ static int __sc401ai_start_stream(struct sc401ai *sc401ai) { int ret; - ret = sc401ai_write_array(sc401ai->client, sc401ai->cur_mode->reg_list); - if (ret) - return ret; + if (!sc401ai->is_thunderboot) { + ret = sc401ai_write_array(sc401ai->client, sc401ai->cur_mode->reg_list); + if (ret) + return ret; - /* In case these controls are set before streaming */ - ret = __v4l2_ctrl_handler_setup(&sc401ai->ctrl_handler); - if (ret) - return ret; + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&sc401ai->ctrl_handler); + if (ret) + return ret; + } return sc401ai_write_reg(sc401ai->client, - SC401AI_REG_CTRL_MODE, - SC401AI_REG_VALUE_08BIT, - SC401AI_MODE_STREAMING); + SC401AI_REG_CTRL_MODE, + SC401AI_REG_VALUE_08BIT, + SC401AI_MODE_STREAMING); + } static int __sc401ai_stop_stream(struct sc401ai *sc401ai) { + if (sc401ai->is_thunderboot) { + sc401ai->is_first_streamoff = true; + pm_runtime_put(&sc401ai->client->dev); + } + return sc401ai_write_reg(sc401ai->client, SC401AI_REG_CTRL_MODE, SC401AI_REG_VALUE_08BIT, @@ -1013,6 +1032,10 @@ static int sc401ai_s_stream(struct v4l2_subdev *sd, int on) goto unlock_and_return; if (on) { + if (sc401ai->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + sc401ai->is_thunderboot = false; + __sc401ai_power_on(sc401ai); + } ret = pm_runtime_get_sync(&client->dev); if (ret < 0) { pm_runtime_put_noidle(&client->dev); @@ -1104,6 +1127,10 @@ static int __sc401ai_power_on(struct sc401ai *sc401ai) dev_err(dev, "Failed to enable xvclk\n"); return ret; } + + if (sc401ai->is_thunderboot) + return 0; + if (!IS_ERR(sc401ai->reset_gpio)) gpiod_set_value_cansleep(sc401ai->reset_gpio, 0); @@ -1142,6 +1169,15 @@ static void __sc401ai_power_off(struct sc401ai *sc401ai) int ret; struct device *dev = &sc401ai->client->dev; + if (sc401ai->is_thunderboot) { + if (sc401ai->is_first_streamoff) { + sc401ai->is_thunderboot = false; + sc401ai->is_first_streamoff = false; + } else { + return; + } + } + if (!IS_ERR(sc401ai->pwdn_gpio)) gpiod_set_value_cansleep(sc401ai->pwdn_gpio, 0); clk_disable_unprepare(sc401ai->xvclk); @@ -1480,6 +1516,11 @@ static int sc401ai_check_sensor_id(struct sc401ai *sc401ai, u32 id = 0; int ret; + if (sc401ai->is_thunderboot) { + dev_info(dev, "Enable thunderboot mode, skip sensor id check\n"); + return 0; + } + ret = sc401ai_read_reg(client, SC401AI_REG_CHIP_ID, SC401AI_REG_VALUE_16BIT, &id); if (id != CHIP_ID) { @@ -1538,6 +1579,8 @@ static int sc401ai_probe(struct i2c_client *client, return -EINVAL; } + sc401ai->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); + sc401ai->client = client; for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { if (hdr_mode == supported_modes[i].hdr_mode) { @@ -1554,13 +1597,23 @@ static int sc401ai_probe(struct i2c_client *client, return -EINVAL; } - sc401ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(sc401ai->reset_gpio)) - dev_warn(dev, "Failed to get reset-gpios\n"); + if (sc401ai->is_thunderboot) { + sc401ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); + if (IS_ERR(sc401ai->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); - sc401ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); - if (IS_ERR(sc401ai->pwdn_gpio)) - dev_warn(dev, "Failed to get pwdn-gpios\n"); + sc401ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS); + if (IS_ERR(sc401ai->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + } else { + sc401ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(sc401ai->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + sc401ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(sc401ai->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + } sc401ai->pinctrl = devm_pinctrl_get(dev); if (!IS_ERR(sc401ai->pinctrl)) { @@ -1635,7 +1688,10 @@ static int sc401ai_probe(struct i2c_client *client, pm_runtime_set_active(dev); pm_runtime_enable(dev); - pm_runtime_idle(dev); + if (sc401ai->is_thunderboot) + pm_runtime_get_sync(dev); + else + pm_runtime_idle(dev); return 0; @@ -1705,7 +1761,12 @@ static void __exit sensor_mod_exit(void) i2c_del_driver(&sc401ai_i2c_driver); } +#if defined(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) && !defined(CONFIG_INITCALL_ASYNC) +subsys_initcall(sensor_mod_init); +#else device_initcall_sync(sensor_mod_init); +#endif + module_exit(sensor_mod_exit); MODULE_DESCRIPTION("smartsens sc401ai sensor driver"); diff --git a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c index dcd49b65ea94..9775b20ba518 100644 --- a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c +++ b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -188,6 +189,8 @@ struct rk_hdmirx_dev { struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler hdl; struct v4l2_ctrl *detect_tx_5v_ctrl; + struct v4l2_ctrl *audio_sampling_rate_ctrl; + struct v4l2_ctrl *audio_present_ctrl; struct v4l2_dv_timings timings; struct gpio_desc *hdmirx_det_gpio; struct work_struct work_wdt_config; @@ -471,6 +474,7 @@ static int hdmirx_subscribe_event(struct v4l2_fh *fh, case V4L2_EVENT_CTRL: return v4l2_ctrl_subscribe_event(fh, sub); case RK_HDMIRX_V4L2_EVENT_SIGNAL_LOST: + case RK_HDMIRX_V4L2_EVENT_AUDIOINFO: return v4l2_event_subscribe(fh, sub, 0, NULL); default: @@ -2353,6 +2357,15 @@ static int hdmirx_register_stream_vdev(struct hdmirx_stream *stream) return 0; } +static void process_audio_change(struct rk_hdmirx_dev *hdmirx_dev) +{ + struct hdmirx_stream *stream = &hdmirx_dev->stream; + const struct v4l2_event evt_audio_info = { + .type = RK_HDMIRX_V4L2_EVENT_AUDIOINFO, + }; + v4l2_event_queue(&stream->vdev, &evt_audio_info); +} + static void process_signal_change(struct rk_hdmirx_dev *hdmirx_dev) { struct hdmirx_stream *stream = &hdmirx_dev->stream; @@ -3248,6 +3261,7 @@ static void hdmirx_delayed_work_audio(struct work_struct *work) if (!hdmirx_dev->audio_present) { dev_info(hdmirx_dev->dev, "audio on"); hdmirx_audio_handle_plugged_change(hdmirx_dev, 1); + process_audio_change(hdmirx_dev); hdmirx_dev->audio_present = true; } if (cur_state - init_state > 16 && cur_state - pre_state > 0) @@ -3258,6 +3272,7 @@ static void hdmirx_delayed_work_audio(struct work_struct *work) if (hdmirx_dev->audio_present) { dev_info(hdmirx_dev->dev, "audio off"); hdmirx_audio_handle_plugged_change(hdmirx_dev, 0); + process_audio_change(hdmirx_dev); hdmirx_dev->audio_present = false; } } @@ -4175,6 +4190,50 @@ static void hdmirx_cancel_cpu_limit_freq(struct rk_hdmirx_dev *hdmirx_dev) dev_err(hdmirx_dev->dev, "%s freq qos nod add\n", __func__); } +static int hdmirx_get_custom_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rk_hdmirx_dev *hdmirx_dev = container_of(ctrl->handler, struct rk_hdmirx_dev, hdl); + int ret = 0; + + if (ctrl->id == RK_V4L2_CID_AUDIO_SAMPLING_RATE) { + *ctrl->p_new.p_s32 = hdmirx_dev->audio_state.fs_audio; + } else if (ctrl->id == RK_V4L2_CID_AUDIO_PRESENT) { + *ctrl->p_new.p_s32 = tx_5v_power_present(hdmirx_dev) ? + hdmirx_dev->audio_present : 0; + } else { + ret = -EINVAL; + } + return ret; +} + +static const struct v4l2_ctrl_ops hdmirx_custom_ctrl_ops = { + .g_volatile_ctrl = hdmirx_get_custom_ctrl, +}; + +static const struct v4l2_ctrl_config hdmirx_ctrl_audio_sampling_rate = { + .ops = &hdmirx_custom_ctrl_ops, + .id = RK_V4L2_CID_AUDIO_SAMPLING_RATE, + .name = "Audio sampling rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 768000, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + +static const struct v4l2_ctrl_config hdmirx_ctrl_audio_present = { + .ops = &hdmirx_custom_ctrl_ops, + .id = RK_V4L2_CID_AUDIO_PRESENT, + .name = "Audio present", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + static int hdmirx_probe(struct platform_device *pdev) { const struct v4l2_dv_timings timings_def = HDMIRX_DEFAULT_TIMING; @@ -4288,10 +4347,19 @@ static int hdmirx_probe(struct platform_device *pdev) strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name)); hdl = &hdmirx_dev->hdl; - v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_handler_init(hdl, 3); hdmirx_dev->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); + /* custom controls */ + hdmirx_dev->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(hdl, + &hdmirx_ctrl_audio_sampling_rate, NULL); + if (hdmirx_dev->audio_sampling_rate_ctrl) + hdmirx_dev->audio_sampling_rate_ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + hdmirx_dev->audio_present_ctrl = v4l2_ctrl_new_custom(hdl, + &hdmirx_ctrl_audio_present, NULL); + if (hdmirx_dev->audio_present_ctrl) + hdmirx_dev->audio_present_ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; if (hdl->error) { dev_err(dev, "v4l2 ctrl handler init failed!\n"); ret = hdl->error; diff --git a/drivers/media/platform/rockchip/isp/common.c b/drivers/media/platform/rockchip/isp/common.c index 04af6d317530..173132560bd7 100644 --- a/drivers/media/platform/rockchip/isp/common.c +++ b/drivers/media/platform/rockchip/isp/common.c @@ -169,8 +169,10 @@ void rkisp_update_regs(struct rkisp_device *dev, u32 start, u32 end) continue; } - if (hw->unite == ISP_UNITE_ONE && dev->unite_index == ISP_UNITE_RIGHT) + if (hw->unite == ISP_UNITE_ONE && dev->unite_index == ISP_UNITE_RIGHT) { val = dev->sw_base_addr + i + RKISP_ISP_SW_MAX_SIZE; + flag = dev->sw_base_addr + i + RKISP_ISP_SW_MAX_SIZE + RKISP_ISP_SW_REG_SIZE; + } if (*flag == SW_REG_CACHE) { if ((i == ISP3X_MAIN_RESIZE_CTRL || diff --git a/drivers/media/platform/rockchip/isp/rkisp.c b/drivers/media/platform/rockchip/isp/rkisp.c index 4a6c0efdd558..98e94343df45 100644 --- a/drivers/media/platform/rockchip/isp/rkisp.c +++ b/drivers/media/platform/rockchip/isp/rkisp.c @@ -1557,6 +1557,7 @@ static void rkisp_config_cmsk_dual(struct rkisp_device *dev, left.win[0].win_en &= ~BIT(i); left.win[1].win_en &= ~BIT(i); left.win[2].win_en &= ~BIT(i); + right.win[i].h_offs = right.win[i].h_offs - w; } else { /* cmsk window at dual isp */ left.win[i].h_size = ALIGN(w - h_offs, 8); diff --git a/drivers/mfd/rkx110_x120/rkx110_x120_core.c b/drivers/mfd/rkx110_x120/rkx110_x120_core.c index c969382abf8f..9a94ee834284 100644 --- a/drivers/mfd/rkx110_x120/rkx110_x120_core.c +++ b/drivers/mfd/rkx110_x120/rkx110_x120_core.c @@ -22,7 +22,7 @@ static const struct mfd_cell rkx110_x120_devs[] = { /* 2 panel device for rkx110_x120 drm panel */ { - .name = "serdes-panel", + .name = "rockchip-serdes-panel", .of_compatible = "rockchip,serdes-panel", }, }; diff --git a/drivers/mfd/rkx110_x120/rkx110_x120_panel.c b/drivers/mfd/rkx110_x120/rkx110_x120_panel.c index eef8bc8863e1..503624e7a92a 100644 --- a/drivers/mfd/rkx110_x120/rkx110_x120_panel.c +++ b/drivers/mfd/rkx110_x120/rkx110_x120_panel.c @@ -630,7 +630,7 @@ static struct platform_driver serdes_panel_driver = { .probe = serdes_panel_probe, .remove = serdes_panel_remove, .driver = { - .name = "serdes-panel", + .name = "rockchip-serdes-panel", .of_match_table = serdes_panel_of_table, }, }; diff --git a/drivers/misc/rockchip/pcie-rkep.c b/drivers/misc/rockchip/pcie-rkep.c index f7cd332365c2..69a3f722bd82 100644 --- a/drivers/misc/rockchip/pcie-rkep.c +++ b/drivers/misc/rockchip/pcie-rkep.c @@ -815,7 +815,12 @@ static void pcie_rkep_remove(struct pci_dev *pdev) #if IS_ENABLED(CONFIG_PCIE_FUNC_RKEP_USERPAGES) free_contig_range(page_to_pfn(pcie_rkep->user_pages), RKEP_USER_MEM_SIZE >> PAGE_SHIFT); #endif - pci_iounmap(pdev, pcie_rkep->bar0); + if (pcie_rkep->bar0) + pci_iounmap(pdev, pcie_rkep->bar0); + if (pcie_rkep->bar2) + pci_iounmap(pdev, pcie_rkep->bar2); + if (pcie_rkep->bar4) + pci_iounmap(pdev, pcie_rkep->bar4); pci_release_regions(pdev); pci_disable_device(pdev); misc_deregister(&pcie_rkep->dev); diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 4e3c6d1c89fc..4b360402c711 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -658,18 +658,6 @@ static inline void rk_pcie_enable_ltssm(struct rk_pcie *rk_pcie) rk_pcie_writel_apb(rk_pcie, 0x0, 0xC000C); } -static int rk_pcie_link_up(struct dw_pcie *pci) -{ - struct rk_pcie *rk_pcie = to_rk_pcie(pci); - u32 val; - - val = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS); - if ((val & (RDLH_LINKUP | SMLH_LINKUP)) == 0x30000) - return 1; - - return 0; -} - static void rk_pcie_enable_debug(struct rk_pcie *rk_pcie) { if (!IS_ENABLED(CONFIG_DEBUG_FS)) @@ -1489,7 +1477,6 @@ MODULE_DEVICE_TABLE(of, rk_pcie_of_match); static const struct dw_pcie_ops dw_pcie_ops = { .start_link = rk_pcie_establish_link, - .link_up = rk_pcie_link_up, }; static void rk_pcie_fast_link_setup(struct rk_pcie *rk_pcie) diff --git a/drivers/regulator/rk806-regulator.c b/drivers/regulator/rk806-regulator.c index a01d7001db00..638753b9fe3a 100644 --- a/drivers/regulator/rk806-regulator.c +++ b/drivers/regulator/rk806-regulator.c @@ -942,6 +942,20 @@ static const struct regulator_ops rk806_ops_ldo = { .set_suspend_disable = rk806_set_suspend_disable, }; +static const struct regulator_ops rk806_ops_ldo6 = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + + .get_voltage_sel = rk806_get_voltage_sel_regmap, + .set_voltage = rk806_set_voltage, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + + .set_ramp_delay = rk806_set_ramp_delay, + + .set_suspend_voltage = rk806_set_suspend_voltage_range, + .resume = rk806_regulator_resume, +}; + #define RK806_REGULATOR(_name, _supply_name, _id, _ops,\ _n_voltages, _vr, _er, _lr, ctrl_bit)\ [_id] = {\ @@ -1033,7 +1047,7 @@ static const struct regulator_desc rk806_regulators[] = { RK806_LDO_SEL_CNT, RK806_PLDO5_ON_VSEL, RK806_POWER_EN5, rk806_ldo_voltage_ranges, 1), - RK806_REGULATOR("PLDO_REG6", "vcca", RK806_ID_PLDO6, rk806_ops_ldo, + RK806_REGULATOR("PLDO_REG6", "vcca", RK806_ID_PLDO6, rk806_ops_ldo6, RK806_LDO_SEL_CNT, RK806_PLDO6_ON_VSEL, RK806_POWER_EN4, rk806_ldo_voltage_ranges, 0), }; diff --git a/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c b/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c index ef7a4b1ee914..cab5d169ffbc 100644 --- a/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c +++ b/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c @@ -1555,7 +1555,7 @@ static int fiq_debugger_probe(struct platform_device *pdev) pr_err("%s: could not install nmi irq handler\n", __func__); irq_clear_status_flags(state->uart_irq, IRQ_NOAUTOEN); ret = request_irq(state->uart_irq, fiq_debugger_uart_irq, - IRQF_NO_SUSPEND, "debug", state); + IRQF_NO_SUSPEND | IRQF_NOBALANCING, "debug", state); } else { enable_nmi(state->uart_irq); } diff --git a/drivers/soc/rockchip/rockchip_amp.c b/drivers/soc/rockchip/rockchip_amp.c index db52d0956ee3..6f5bdc678756 100644 --- a/drivers/soc/rockchip/rockchip_amp.c +++ b/drivers/soc/rockchip/rockchip_amp.c @@ -15,6 +15,7 @@ #include #include #include +#include #define RK_CPU_STATUS_OFF 0 #define RK_CPU_STATUS_ON 1 @@ -24,6 +25,7 @@ #define GPIO_BANK_NUM 16 #define GPIO_GROUP_PRIO_MAX 3 +#define MAX_GIC_SPI_NUM (1020) #define AMP_GIC_DBG(fmt, arg...) do { if (0) { pr_warn(fmt, ##arg); } } while (0) enum amp_cpu_ctrl_status { @@ -55,26 +57,27 @@ static struct { struct amp_gpio_group_s { u32 bank_id; u32 prio; - u32 irq_aff[AMP_AFF_MAX_CPU]; + u64 irq_aff[AMP_AFF_MAX_CPU]; u32 irq_id[AMP_AFF_MAX_CPU]; u32 en[AMP_AFF_MAX_CPU]; }; struct amp_irq_cfg_s { + u64 aff; u32 prio; u32 cpumask; - u32 aff; int amp_flag; -} irqs_cfg[1024]; +} irqs_cfg[MAX_GIC_SPI_NUM]; static struct amp_gic_ctrl_s { + enum gic_type gic_version; + u32 spis_num; struct { u32 aff; u32 cpumask; u32 flag; } aff_to_cpumask[AMP_AFF_MAX_CLUSTER][AMP_AFF_MAX_CPU]; - struct amp_irq_cfg_s irqs_cfg[1024]; - u32 validmask[1020 / 32 + 1]; + struct amp_irq_cfg_s irqs_cfg[MAX_GIC_SPI_NUM]; struct amp_gpio_group_s gpio_grp[GPIO_BANK_NUM][GPIO_GROUP_PRIO_MAX]; u32 gpio_banks; } amp_ctrl; @@ -275,7 +278,12 @@ u32 rockchip_amp_get_irq_cpumask(u32 irq) return amp_ctrl.irqs_cfg[irq].cpumask; } -static u32 amp_get_cpumask_bit(u32 aff) +int rockchip_amp_need_init_amp_irq(u32 irq) +{ + return amp_ctrl.irqs_cfg[irq].amp_flag; +} + +static u32 amp_get_cpumask_bit(u64 aff) { u32 aff_cluster, aff_cpu; @@ -285,18 +293,24 @@ static u32 amp_get_cpumask_bit(u32 aff) if (aff_cpu >= AMP_AFF_MAX_CPU || aff_cluster >= AMP_AFF_MAX_CLUSTER) return 0; - AMP_GIC_DBG("%s: aff:%d-%d: %x\n", __func__, aff_cluster, aff_cpu, + AMP_GIC_DBG(" %s: aff:%d-%d: %x\n", __func__, aff_cluster, aff_cpu, amp_ctrl.aff_to_cpumask[aff_cluster][aff_cpu].cpumask); return amp_ctrl.aff_to_cpumask[aff_cluster][aff_cpu].cpumask; } +u64 rockchip_amp_get_irq_aff(u32 irq) +{ + return amp_ctrl.irqs_cfg[irq].aff; +} + static int gic_amp_get_gpio_prio_group_info(struct device_node *np, struct amp_gic_ctrl_s *amp_ctrl, int prio_id) { - u32 gpio_bank, count0, count1, prio, irq_id, irq_aff; - int i; + u32 gpio_bank, prio, irq_id; + u64 irq_aff; + int i, count0, count1; struct amp_gpio_group_s *gpio_grp; struct amp_irq_cfg_s *irqs_cfg; @@ -320,7 +334,7 @@ static int gic_amp_get_gpio_prio_group_info(struct device_node *np, __func__, gpio_bank, prio_id, prio); count0 = of_property_count_u32_elems(np, "girq-id"); - count1 = of_property_count_u32_elems(np, "girq-aff"); + count1 = of_property_count_u64_elems(np, "girq-aff"); if (count0 != count1) return -EINVAL; @@ -330,7 +344,7 @@ static int gic_amp_get_gpio_prio_group_info(struct device_node *np, for (i = 0; i < count0; i++) { of_property_read_u32_index(np, "girq-id", i, &irq_id); gpio_grp->irq_id[i] = irq_id; - of_property_read_u32_index(np, "girq-aff", i, &irq_aff); + of_property_read_u64_index(np, "girq-aff", i, &irq_aff); gpio_grp->irq_aff[i] = irq_aff; @@ -338,18 +352,24 @@ static int gic_amp_get_gpio_prio_group_info(struct device_node *np, irqs_cfg = &_ctrl->irqs_cfg[irq_id]; - AMP_GIC_DBG(" %s: group cpu-%d, irq-%d: prio-%x, aff-%x en-%d\n", + AMP_GIC_DBG(" %s: group cpu-%d, irq-%d: prio-%x, aff-%llx en-%d\n", __func__, i, gpio_grp->irq_id[i], gpio_grp->prio, gpio_grp->irq_aff[i], gpio_grp->en[i]); if (gpio_grp->en[i]) { irqs_cfg->prio = gpio_grp->prio; irqs_cfg->aff = irq_aff; - irqs_cfg->cpumask = amp_get_cpumask_bit(irq_aff); + if (amp_ctrl->gic_version == GIC_V2) { + irqs_cfg->cpumask = amp_get_cpumask_bit(irq_aff); + if (!irqs_cfg->cpumask) { + pr_err(" %s: get cpumask error\n", __func__); + return -EINVAL; + } + } irqs_cfg->amp_flag = 1; } - AMP_GIC_DBG(" %s: irqs_cfg prio-%x aff-%x cpumaks-%x en-%d\n", + AMP_GIC_DBG(" %s: prio-%x aff-%llx cpumaks-%x flag-%d\n", __func__, irqs_cfg->prio, irqs_cfg->aff, irqs_cfg->cpumask, irqs_cfg->amp_flag); } @@ -404,33 +424,37 @@ static int amp_gic_get_cpumask(struct device_node *np, struct amp_gic_ctrl_s *am { const struct property *prop; int count, i; - u32 cluster, aff_cpu, aff, cpumask; + u32 cluster, aff_cpu; + u64 aff, cpumask; + if (amp_ctrl->gic_version != GIC_V2) + return 0; prop = of_find_property(np, "amp-cpu-aff-maskbits", NULL); + if (!prop) return -1; if (!prop->value) return -1; - count = of_property_count_u32_elems(np, "amp-cpu-aff-maskbits"); + count = of_property_count_u64_elems(np, "amp-cpu-aff-maskbits"); if (count % 2) return -1; for (i = 0; i < count / 2; i++) { - of_property_read_u32_index(np, "amp-cpu-aff-maskbits", + of_property_read_u64_index(np, "amp-cpu-aff-maskbits", 2 * i, &aff); cluster = MPIDR_AFFINITY_LEVEL(aff, 1); aff_cpu = MPIDR_AFFINITY_LEVEL(aff, 0); amp_ctrl->aff_to_cpumask[cluster][aff_cpu].aff = aff; - of_property_read_u32_index(np, "amp-cpu-aff-maskbits", + of_property_read_u64_index(np, "amp-cpu-aff-maskbits", 2 * i + 1, &cpumask); - amp_ctrl->aff_to_cpumask[cluster][aff_cpu].cpumask = cpumask; + amp_ctrl->aff_to_cpumask[cluster][aff_cpu].cpumask = (u32)cpumask; - AMP_GIC_DBG("cpumask: %d-%d: aff-%d cpumask-%d\n", - cluster, aff_cpu, aff, cpumask); + AMP_GIC_DBG("cpumask: %d-%d: aff-%llx cpumask-%d\n", + cluster, aff_cpu, aff, (u32)cpumask); if (!cpumask) return -1; @@ -443,8 +467,9 @@ static void amp_gic_get_irqs_config(struct device_node *np, struct amp_gic_ctrl_s *amp_ctrl) { const struct property *prop; - int count, i; - u32 irq, prio, aff; + u32 irq, i; + int count; + u64 aff, val, prio; prop = of_find_property(np, "amp-irqs", NULL); if (!prop) @@ -453,30 +478,31 @@ static void amp_gic_get_irqs_config(struct device_node *np, if (!prop->value) return; - count = of_property_count_u32_elems(np, "amp-irqs"); + count = of_property_count_u64_elems(np, "amp-irqs"); - if (count < 0 || count % 3) + if (count % 3) return; for (i = 0; i < count / 3; i++) { - of_property_read_u32_index(np, "amp-irqs", 3 * i, &irq); - - if (irq > 1020) + of_property_read_u64_index(np, "amp-irqs", 3 * i, &val); + irq = (u32)val; + if (irq > amp_ctrl->spis_num) break; - of_property_read_u32_index(np, "amp-irqs", 3 * i + 1, &prio); - of_property_read_u32_index(np, "amp-irqs", 3 * i + 2, &aff); + of_property_read_u64_index(np, "amp-irqs", 3 * i + 1, &prio); + of_property_read_u64_index(np, "amp-irqs", 3 * i + 2, &aff); - AMP_GIC_DBG("%s: irq-%d aff-%d prio-%x\n", + AMP_GIC_DBG("%s: irq-%d aff-%llx prio-%llx\n", __func__, irq, aff, prio); - amp_ctrl->irqs_cfg[irq].prio = prio; + amp_ctrl->irqs_cfg[irq].prio = (u32)prio; amp_ctrl->irqs_cfg[irq].aff = aff; - amp_ctrl->irqs_cfg[irq].cpumask = amp_get_cpumask_bit(aff); - - if (!amp_ctrl->irqs_cfg[irq].cpumask) { - AMP_GIC_DBG("%s: get cpumask error\n", __func__); - break; + if (amp_ctrl->gic_version == GIC_V2) { + amp_ctrl->irqs_cfg[irq].cpumask = amp_get_cpumask_bit(aff); + if (!amp_ctrl->irqs_cfg[irq].cpumask) { + pr_err("%s: get cpumask error\n", __func__); + break; + } } if (!amp_ctrl->irqs_cfg[irq].aff && @@ -485,17 +511,20 @@ static void amp_gic_get_irqs_config(struct device_node *np, amp_ctrl->irqs_cfg[irq].amp_flag = 1; - AMP_GIC_DBG("%s: irq-%d aff-%d cpumask-%d pri-%x\n", + AMP_GIC_DBG(" %s: irq-%d aff-%llx cpumask-%x pri-%x\n", __func__, irq, amp_ctrl->irqs_cfg[irq].aff, amp_ctrl->irqs_cfg[irq].cpumask, amp_ctrl->irqs_cfg[irq].prio); } } -void rockchip_amp_get_gic_info(void) +void rockchip_amp_get_gic_info(u32 spis_num, enum gic_type gic_version) { struct device_node *np; + amp_ctrl.spis_num = spis_num; + amp_ctrl.gic_version = gic_version; + np = of_find_node_by_name(NULL, "rockchip-amp"); if (!np) return; @@ -504,6 +533,7 @@ void rockchip_amp_get_gic_info(void) pr_err("%s: get amp gic cpu mask error\n", __func__); goto exit; } + gic_of_get_gpio_group(np, &_ctrl); amp_gic_get_irqs_config(np, &_ctrl); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index be041bd65b19..14ee58e6b78a 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1285,7 +1285,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) * the first wake signalling failed, give it that chance. */ pending_portevent = xhci_pending_portevent(xhci); - if (!pending_portevent) { + if (!pending_portevent && !IS_ENABLED(CONFIG_ARCH_ROCKCHIP)) { msleep(120); pending_portevent = xhci_pending_portevent(xhci); } diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig index 2b5a072d2cdd..571d34c08603 100644 --- a/drivers/video/rockchip/Kconfig +++ b/drivers/video/rockchip/Kconfig @@ -7,3 +7,4 @@ source "drivers/video/rockchip/iep/Kconfig" source "drivers/video/rockchip/mpp/Kconfig" source "drivers/video/rockchip/dvbm/Kconfig" source "drivers/video/rockchip/vehicle/Kconfig" +source "drivers/video/rockchip/vtunnel/Kconfig" diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index 0b8a7bbb95e6..b13d93a1cd6f 100644 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_IEP) += iep/ obj-$(CONFIG_ROCKCHIP_MPP_SERVICE) += mpp/ obj-$(CONFIG_ROCKCHIP_DVBM) += dvbm/ obj-$(CONFIG_VIDEO_REVERSE_IMAGE) += vehicle/ +obj-$(CONFIG_ROCKCHIP_VIDEO_TUNNEL) += vtunnel/ diff --git a/drivers/video/rockchip/mpp/mpp_iommu.c b/drivers/video/rockchip/mpp/mpp_iommu.c index 5bec7cf15956..cc206475add8 100644 --- a/drivers/video/rockchip/mpp/mpp_iommu.c +++ b/drivers/video/rockchip/mpp/mpp_iommu.c @@ -461,6 +461,12 @@ static int mpp_iommu_handle(struct iommu_domain *iommu, else mpp_task_dump_hw_reg(mpp); + /* + * Mask iommu irq, in order for iommu not repeatedly trigger pagefault. + * Until the pagefault task finish by hw timeout. + */ + rockchip_iommu_mask_irq(mpp->dev); + return 0; } diff --git a/drivers/video/rockchip/mpp/mpp_rkvenc2.c b/drivers/video/rockchip/mpp/mpp_rkvenc2.c index dd1e74183ecd..5c1120a5054a 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvenc2.c +++ b/drivers/video/rockchip/mpp/mpp_rkvenc2.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "mpp_debug.h" #include "mpp_iommu.h" @@ -43,6 +44,8 @@ #define RKVENC_MAX_DCHS_ID 4 #define RKVENC_MAX_SLICE_FIFO_LEN 256 #define RKVENC_SCLR_DONE_STA BIT(2) +#define RKVENC_WDG 0x38 +#define TIMEOUT_MS 100 #define to_rkvenc_info(info) \ container_of(info, struct rkvenc_hw_info, hw) @@ -1193,6 +1196,7 @@ static int rkvenc_run(struct mpp_dev *mpp, struct mpp_task *mpp_task) struct rkvenc_task *task = to_rkvenc_task(mpp_task); struct rkvenc_hw_info *hw = enc->hw_info; u32 timing_en = mpp->srv->timing_en; + u32 timeout_thd; mpp_debug_enter(); @@ -1246,6 +1250,13 @@ static int rkvenc_run(struct mpp_dev *mpp, struct mpp_task *mpp_task) /* Flush the register before the start the device */ wmb(); + /* + * reconfig timeout threshold. + * bit0-bit23,x1024 core clk cycles + */ + timeout_thd = mpp_read(mpp, RKVENC_WDG) & 0xff000000; + timeout_thd |= TIMEOUT_MS * clk_get_rate(enc->core_clk_info.clk) / 1024000; + mpp_write(mpp, RKVENC_WDG, timeout_thd); mpp_write(mpp, enc->hw_info->enc_start_base, start_val); mpp_task_run_end(mpp_task, timing_en); @@ -1457,7 +1468,7 @@ static int rkvenc_finish(struct mpp_dev *mpp, struct mpp_task *mpp_task) if (task->bs_buf) { u32 bs_size = mpp_read(mpp, 0x4064); - mpp_dma_buf_sync(task->bs_buf, 0, bs_size / 8 + task->offset_bs, + mpp_dma_buf_sync(task->bs_buf, 0, bs_size + task->offset_bs, DMA_FROM_DEVICE, true); } @@ -1862,7 +1873,7 @@ static int rkvenc_soft_reset(struct mpp_dev *mpp) /* safe reset */ mpp_write(mpp, hw->int_mask_base, 0x3FF); - mpp_write(mpp, hw->enc_clr_base, 0x1); + mpp_write(mpp, hw->enc_clr_base, 0x3); ret = readl_relaxed_poll_timeout(mpp->reg_base + hw->int_sta_base, rst_status, rst_status & RKVENC_SCLR_DONE_STA, @@ -2386,14 +2397,33 @@ static int rkvenc2_iommu_fault_handle(struct iommu_domain *iommu, { struct mpp_dev *mpp = (struct mpp_dev *)arg; struct rkvenc_dev *enc = to_rkvenc_dev(mpp); - struct mpp_task *mpp_task = mpp->cur_task; + struct mpp_task *mpp_task; + struct rkvenc_ccu *ccu = enc->ccu; + if (ccu) { + struct rkvenc_dev *core = NULL, *n; + + list_for_each_entry_safe(core, n, &ccu->core_list, core_link) { + if (core->mpp.iommu_info && + (&core->mpp.iommu_info->pdev->dev == iommu_dev)) { + mpp = &core->mpp; + break; + } + } + } + mpp_task = mpp->cur_task; dev_info(mpp->dev, "core %d page fault found dchs %08x\n", mpp->core_id, mpp_read_relaxed(&enc->mpp, DCHS_REG_OFFSET)); if (mpp_task) mpp_task_dump_mem_region(mpp, mpp_task); + /* + * Mask iommu irq, in order for iommu not repeatedly trigger pagefault. + * Until the pagefault task finish by hw timeout. + */ + rockchip_iommu_mask_irq(mpp->dev); + return 0; } diff --git a/drivers/video/rockchip/mpp/mpp_vepu2.c b/drivers/video/rockchip/mpp/mpp_vepu2.c index 395a5b69333f..fb5f5e5c28e3 100644 --- a/drivers/video/rockchip/mpp/mpp_vepu2.c +++ b/drivers/video/rockchip/mpp/mpp_vepu2.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "mpp_debug.h" #include "mpp_common.h" @@ -882,6 +883,48 @@ static int vepu_reset(struct mpp_dev *mpp) return 0; } +static int vepu2_iommu_fault_handle(struct iommu_domain *iommu, struct device *iommu_dev, + unsigned long iova, int status, void *arg) +{ + struct mpp_dev *mpp = (struct mpp_dev *)arg; + struct mpp_task *mpp_task; + struct vepu_dev *enc = to_vepu_dev(mpp); + struct vepu_ccu *ccu = enc->ccu; + + dev_err(iommu_dev, "fault addr 0x%08lx status %x arg %p\n", + iova, status, arg); + + if (ccu) { + int i; + struct mpp_dev *core; + + for (i = 0; i < ccu->core_num; i++) { + core = ccu->cores[i]; + if (core->iommu_info && (&core->iommu_info->pdev->dev == iommu_dev)) { + mpp = core; + break; + } + } + } + + if (!mpp) { + dev_err(iommu_dev, "pagefault without device to handle\n"); + return 0; + } + mpp_task = mpp->cur_task; + if (mpp_task) + mpp_task_dump_mem_region(mpp, mpp_task); + + mpp_task_dump_hw_reg(mpp); + /* + * Mask iommu irq, in order for iommu not repeatedly trigger pagefault. + * Until the pagefault task finish by hw timeout. + */ + rockchip_iommu_mask_irq(mpp->dev); + + return 0; +} + static struct mpp_hw_ops vepu_v2_hw_ops = { .init = vepu_init, .clk_on = vepu_clk_on, @@ -1100,6 +1143,7 @@ static int vepu_core_probe(struct platform_device *pdev) return -EINVAL; } + mpp->fault_handler = vepu2_iommu_fault_handle; mpp->session_max_buffers = VEPU2_SESSION_MAX_BUFFERS; vepu_procfs_init(mpp); vepu_procfs_ccu_init(mpp); @@ -1149,6 +1193,7 @@ static int vepu_probe_default(struct platform_device *pdev) return -EINVAL; } + mpp->fault_handler = vepu2_iommu_fault_handle; mpp->session_max_buffers = VEPU2_SESSION_MAX_BUFFERS; vepu_procfs_init(mpp); /* register current device to mpp service */ diff --git a/drivers/video/rockchip/rga2/rga2_drv.c b/drivers/video/rockchip/rga2/rga2_drv.c index 9aeb89ac682a..ca5218f3e09f 100644 --- a/drivers/video/rockchip/rga2/rga2_drv.c +++ b/drivers/video/rockchip/rga2/rga2_drv.c @@ -76,7 +76,6 @@ #define RGA2_PHY_PAGE_SIZE (((8192 * 8192 * 4) / 4096) + 1) ktime_t rga2_start; -ktime_t rga2_end; int rga2_flag; int first_RGA2_proc; static int rk3368; @@ -1179,9 +1178,11 @@ retry: #ifdef CONFIG_ROCKCHIP_RGA2_DEBUGGER if (RGA2_TEST_TIME) { - rga2_end = ktime_get(); - rga2_end = ktime_sub(rga2_end, rga2_start); - DBG("sync one cmd end time %d\n", (int)ktime_to_us(rga2_end)); + ktime_t rga2_cmd_end; + + rga2_cmd_end = ktime_get(); + rga2_cmd_end = ktime_sub(rga2_cmd_end, rga2_start); + DBG("sync one cmd end time %d us\n", (int)ktime_to_us(rga2_cmd_end)); } #endif if (ret == -ETIMEDOUT && try--) { @@ -1657,6 +1658,14 @@ static irqreturn_t rga2_irq_thread(int irq, void *dev_id) if (RGA2_INT_FLAG) INFO("irqthread INT[%x],STATS[%x]\n", rga2_read(RGA2_INT), rga2_read(RGA2_STATUS)); + + if (RGA2_TEST_TIME) { + ktime_t rga2_hw_end; + + rga2_hw_end = ktime_get(); + rga2_hw_end = ktime_sub(rga2_hw_end, rga2_start); + DBG("RGA hardware cost time %d us\n", (int)ktime_to_us(rga2_hw_end)); + } #endif RGA2_flush_page(); mutex_lock(&rga2_service.lock); diff --git a/drivers/video/rockchip/vtunnel/Kconfig b/drivers/video/rockchip/vtunnel/Kconfig new file mode 100644 index 000000000000..49ad2a592bb0 --- /dev/null +++ b/drivers/video/rockchip/vtunnel/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +menu "Rockchip video tunnel support" + +config ROCKCHIP_VIDEO_TUNNEL + tristate "Rockchip video tunnel device support" + depends on ARCH_ROCKCHIP + default n + help + Rockchip videotunnel device support. + +endmenu diff --git a/drivers/video/rockchip/vtunnel/Makefile b/drivers/video/rockchip/vtunnel/Makefile new file mode 100644 index 000000000000..fdfd79a1c0a2 --- /dev/null +++ b/drivers/video/rockchip/vtunnel/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_ROCKCHIP_VIDEO_TUNNEL) += rkvtunnel.o diff --git a/drivers/video/rockchip/vtunnel/rkvtunnel.c b/drivers/video/rockchip/vtunnel/rkvtunnel.c new file mode 100644 index 000000000000..a2ce451b7cda --- /dev/null +++ b/drivers/video/rockchip/vtunnel/rkvtunnel.c @@ -0,0 +1,1523 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rkvtunnel.h" + +#define DEVICE_NAME "rkvtunnel" +#define RKVT_MAX_NAME_LENGTH 128 +#define RKVT_POOL_SIZE 32 +#define RKVT_MAX_WAIT_MS 4 +#define RKVT_FENCE_WAIT_MS 3000 + +#define RKVT_DBG_USER (1U << 0) +#define RKVT_DBG_BUFFERS (1U << 1) +#define RKVT_DBG_CMD (1U << 2) +#define RKVT_DBG_FILE (1U << 3) + +#define rkvt_dbg(mask, x...)\ + do { if (unlikely(vt_dev_dbg & mask)) pr_info(x); } while (0) + +enum rkvt_buf_status_e { + RKVT_BUF_QUEUE, + RKVT_BUF_DEQUEUE, + RKVT_BUF_ACQUIRE, + RKVT_BUF_RELEASE, + RKVT_BUF_FREE, + RKVT_BUF_BUTT, +}; + +union rkvt_ioc_arg { + struct rkvt_alloc_id_data alloc_data; + struct rkvt_ctrl_data ctrl_data; + struct rkvt_buf_data buffer_data; +}; + +struct rkvt_dev { + struct device *dev; + struct miscdevice mdev; + struct mutex inst_lock; /* protect inst_list and ints_idr */ + struct idr inst_idr; + struct list_head list_inst; /* manage all instances */ + + struct mutex session_lock; /* protect sessions */ + struct list_head list_session; + + char *dev_name; + int inst_id_generator; + atomic64_t cid_generator; + atomic64_t buf_id_generator; + struct dentry *debug_root; +}; + +struct rkvt_session { + struct list_head dev_link; + struct rkvt_dev *vt_dev; + struct list_head list_inst; /* manage instance in session */ + + enum rkvt_caller_e caller; + pid_t pid; + char name[RKVT_MAX_NAME_LENGTH]; + char disp_name[RKVT_MAX_NAME_LENGTH]; + int disp_serial; + int cid; + struct task_struct *task; + struct dentry *debug_root; +}; + +struct rkvt_buffer { + struct file *file_buf[MAX_BUF_HANDLE_FDS]; + int fds_pro[MAX_BUF_HANDLE_FDS]; + int fds_con[MAX_BUF_HANDLE_FDS]; + + struct file *ready_render_fence; + struct dma_fence *rendered_fence; + struct rkvt_session *session_pro; + int cid_pro; + struct rkvt_buf_base base; +}; + +struct rkvt_instance { + struct kref ref; + int id; + struct rkvt_dev *vt_dev; + + struct mutex lock; + struct list_head dev_link; + struct list_head session_link; + struct rkvt_session *consumer; + struct rkvt_session *producer; + wait_queue_head_t wait_consumer; + wait_queue_head_t wait_producer; + + struct dentry *debug_root; + int fcount; + + DECLARE_KFIFO_PTR(fifo_to_consumer, struct rkvt_buffer*); + DECLARE_KFIFO_PTR(fifo_to_producer, struct rkvt_buffer*); + + struct rkvt_buffer vt_buffers[RKVT_POOL_SIZE]; +}; + +static unsigned int vt_dev_dbg; + +module_param(vt_dev_dbg, uint, 0644); +MODULE_PARM_DESC(vt_dev_dbg, "bit switch for vt debug information"); + +static const char * +rkvt_dbg_buf_status_to_string(int status) +{ + const char *status_str; + + switch (status) { + case RKVT_BUF_QUEUE: + status_str = "queued"; + break; + case RKVT_BUF_DEQUEUE: + status_str = "dequeued"; + break; + case RKVT_BUF_ACQUIRE: + status_str = "acquired"; + break; + case RKVT_BUF_RELEASE: + status_str = "released"; + break; + case RKVT_BUF_FREE: + status_str = "free"; + break; + default: + status_str = "unknown"; + } + + return status_str; +} + +static int rkvt_dbg_instance_show(struct seq_file *s, void *unused) +{ + struct rkvt_instance *inst = s->private; + int i; + int size_to_con; + int size_to_pro; + int ref_count; + + mutex_lock(&inst->lock); + size_to_con = kfifo_len(&inst->fifo_to_consumer); + size_to_pro = kfifo_len(&inst->fifo_to_producer); + ref_count = kref_read(&inst->ref); + + seq_printf(s, "tunnel (%p) id=%d, ref=%d, fcount=%d\n", + inst, inst->id, ref_count, inst->fcount); + seq_puts(s, "-----------------------------------------------\n"); + if (inst->consumer) + seq_printf(s, "consumer session (%s) %p\n", + inst->consumer->disp_name, inst->consumer); + if (inst->producer) + seq_printf(s, "producer session (%s) %p\n", + inst->producer->disp_name, inst->producer); + seq_puts(s, "-----------------------------------------------\n"); + + seq_printf(s, "to consumer fifo size:%d\n", size_to_con); + seq_printf(s, "to producer fifo size:%d\n", size_to_pro); + seq_puts(s, "-----------------------------------------------\n"); + + seq_puts(s, "buffers:\n"); + + for (i = 0; i < RKVT_POOL_SIZE; i++) { + struct rkvt_buffer *buffer = &inst->vt_buffers[i]; + int status = buffer->base.buf_status; + + seq_printf(s, " buffer produce_fd[0](%d) status(%s)\n", + buffer->fds_pro[0], + rkvt_dbg_buf_status_to_string(status)); + } + seq_puts(s, "-----------------------------------------------\n"); + mutex_unlock(&inst->lock); + + return 0; +} + +static int +rkvt_dbg_instance_open(struct inode *inode, struct file *file) +{ + return single_open(file, + rkvt_dbg_instance_show, + inode->i_private); +} + +static const struct file_operations dbg_instance_fops = { + .open = rkvt_dbg_instance_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int rkvt_dbg_session_show(struct seq_file *s, void *unused) +{ + struct rkvt_session *session = s->private; + + seq_printf(s, "session(%s) %p role %s cid %d\n", + session->disp_name, session, + session->caller == RKVT_CALLER_PRODUCER ? + "producer" : (session->caller == RKVT_CALLER_CONSUMER ? + "consumer" : "invalid"), session->cid); + seq_puts(s, "-----------------------------------------------\n"); + + return 0; +} + +static int rkvt_dbg_session_open(struct inode *inode, struct file *file) +{ + return single_open(file, + rkvt_dbg_session_show, + inode->i_private); +} + +static const struct file_operations debug_session_fops = { + .open = rkvt_dbg_session_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __rkvt_close_fd(struct files_struct *files, unsigned int fd) +{ + struct file *file; + struct fdtable *fdt; + + spin_lock(&files->file_lock); + + fdt = files_fdtable(files); + if (fd >= fdt->max_fds) + goto out_unlock; + file = fdt->fd[fd]; + if (!file) + goto out_unlock; + + rcu_assign_pointer(fdt->fd[fd], NULL); + spin_unlock(&files->file_lock); + + put_unused_fd(fd); + return filp_close(file, files); + +out_unlock: + spin_unlock(&files->file_lock); + return -EBADF; +} + +static int rkvt_close_fd(struct rkvt_session *session, unsigned int fd) +{ + int ret; + + if (!session->task) + return -ESRCH; + + ret = __rkvt_close_fd(session->task->files, fd); + if (unlikely(ret == -ERESTARTSYS || + ret == -ERESTARTNOINTR || + ret == -ERESTARTNOHAND || + ret == -ERESTART_RESTARTBLOCK)) + ret = -EINTR; + + return ret; +} + +/* The function is responsible for fifo_to_consumer fifo operation + * requires external use of rkvt_instance.lock protection + */ +static void rkvt_inst_clear_consumer(struct rkvt_instance *inst) +{ + struct rkvt_buffer *buffer = NULL; + int i = 0; + + if (!inst) + return; + + while (kfifo_get(&inst->fifo_to_consumer, &buffer)) { + /* put file */ + for (i = 0; i < buffer->base.num_fds; i++) { + if (buffer->file_buf[i]) { + fput(buffer->file_buf[i]); + buffer->file_buf[i] = NULL; + } + inst->fcount--; + } + if (buffer->ready_render_fence) { + fput(buffer->ready_render_fence); + buffer->ready_render_fence = NULL; + } + rkvt_dbg(RKVT_DBG_FILE, + "vt [%d] instance trim file(%p) buffer(%p) ino(%08lu) fcount=%d\n", + inst->id, buffer->file_buf, buffer, + buffer->file_buf[i] ? + file_inode(buffer->file_buf[i])->i_ino : 0, + inst->fcount); + if (inst->producer != NULL) { + buffer->base.buf_status = RKVT_BUF_RELEASE; + kfifo_put(&inst->fifo_to_producer, buffer); + wake_up_interruptible(&inst->wait_producer); + } else { + buffer->base.buf_status = RKVT_BUF_FREE; + } + } +} + +/* The function is responsible for fifo_to_consumer fifo operation + * requires external use of rkvt_instance.lock protection. + */ +static void rkvt_inst_clear_producer(struct rkvt_instance *inst) +{ + struct rkvt_buffer *buffer = NULL; + + if (!inst) + return; + + while (kfifo_get(&inst->fifo_to_producer, &buffer)) { + if (buffer->rendered_fence) { + dma_fence_put(buffer->rendered_fence); + buffer->rendered_fence = NULL; + } + buffer->base.buf_status = RKVT_BUF_FREE; + } +} + +static void rkvt_inst_destroy(struct kref *kref) +{ + struct rkvt_instance *inst = + container_of(kref, struct rkvt_instance, ref); + struct rkvt_dev *vt_dev = inst->vt_dev; + + list_del_init(&inst->dev_link); + idr_remove(&vt_dev->inst_idr, inst->id); + + rkvt_dbg(RKVT_DBG_USER, "vt [%d] destroy\n", inst->id); + + mutex_lock(&inst->lock); + rkvt_inst_clear_consumer(inst); + rkvt_inst_clear_producer(inst); + kfifo_free(&inst->fifo_to_consumer); + kfifo_free(&inst->fifo_to_producer); + mutex_unlock(&inst->lock); + + debugfs_remove_recursive(inst->debug_root); + + devm_kfree(vt_dev->dev, inst); +} + +static struct rkvt_instance *rkvt_inst_create(struct rkvt_dev *vt_dev) +{ + struct rkvt_instance *inst; + int status; + int i; + + inst = devm_kzalloc(vt_dev->dev, sizeof(*inst), GFP_KERNEL); + if (!inst) + return ERR_PTR(-ENOMEM); + + inst->vt_dev = vt_dev; + mutex_init(&inst->lock); + INIT_LIST_HEAD(&inst->dev_link); + INIT_LIST_HEAD(&inst->session_link); + kref_init(&inst->ref); + + status = kfifo_alloc(&inst->fifo_to_consumer, + RKVT_POOL_SIZE, GFP_KERNEL); + if (status) + goto setup_fail; + + status = kfifo_alloc(&inst->fifo_to_producer, + RKVT_POOL_SIZE, GFP_KERNEL); + if (status) + goto fifo_alloc_fail; + + init_waitqueue_head(&inst->wait_producer); + init_waitqueue_head(&inst->wait_consumer); + + for (i = 0; i < RKVT_POOL_SIZE; i++) + inst->vt_buffers[i].base.buf_status = RKVT_BUF_FREE; + + /* insert it to dev instances list */ + mutex_lock(&vt_dev->inst_lock); + list_add_tail(&inst->dev_link, &vt_dev->list_inst); + mutex_unlock(&vt_dev->inst_lock); + + return inst; +fifo_alloc_fail: + kfifo_free(&inst->fifo_to_consumer); +setup_fail: + devm_kfree(vt_dev->dev, inst); + return ERR_PTR(status); +} + +/* The function protected by rkvt_dev.session_lock by caller */ +static int +rkvt_get_session_serial(const struct list_head *sessions, + const unsigned char *name) +{ + int serial = -1; + struct rkvt_session *session, *n; + + list_for_each_entry_safe(session, n, sessions, dev_link) { + if (strcmp(session->name, name)) + continue; + serial = max(serial, session->disp_serial); + } + + return serial + 1; +} + +/* The function protected by rkvt_instance.lock by caller */ +static void +rkvt_session_trim_locked(struct rkvt_session *session, struct rkvt_instance *inst) +{ + if (!session || !inst) + return; + + if (inst->producer && inst->producer == session) { + rkvt_inst_clear_producer(inst); + inst->producer = NULL; + } + + if (inst->consumer && inst->consumer == session) { + rkvt_inst_clear_consumer(inst); + inst->consumer = NULL; + } +} + +static int rkvt_inst_trim(struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst, *n; + int i; + + mutex_lock(&vt_dev->inst_lock); + list_for_each_entry_safe(inst, n, &vt_dev->list_inst, dev_link) { + mutex_lock(&inst->lock); + rkvt_session_trim_locked(session, inst); + + if (!inst->consumer && !inst->producer) { + rkvt_inst_clear_producer(inst); + rkvt_inst_clear_consumer(inst); + + for (i = 0; i < RKVT_POOL_SIZE; i++) + inst->vt_buffers[i].base.buf_status = RKVT_BUF_FREE; + } + mutex_unlock(&inst->lock); + } + mutex_unlock(&vt_dev->inst_lock); + + return 0; +} + +static struct rkvt_session * +rkvt_session_create(struct rkvt_dev *vt_dev, const char *name) +{ + struct rkvt_session *session; + struct task_struct *task = NULL; + + if (!name) { + dev_err(vt_dev->dev, "%s: Name can not be null\n", __func__); + return ERR_PTR(-EINVAL); + } + + session = devm_kzalloc(vt_dev->dev, sizeof(*session), GFP_KERNEL); + if (!session) + return ERR_PTR(-ENOMEM); + + get_task_struct(current->group_leader); + task_lock(current->group_leader); + session->pid = task_pid_nr(current->group_leader); + + if (current->group_leader->flags & PF_KTHREAD) { + put_task_struct(current->group_leader); + task = NULL; + } else { + task = current->group_leader; + } + + task_unlock(current->group_leader); + + session->vt_dev = vt_dev; + session->task = task; + session->caller = RKVT_CALLER_BUTT; + INIT_LIST_HEAD(&session->dev_link); + INIT_LIST_HEAD(&session->list_inst); + snprintf(session->name, RKVT_MAX_NAME_LENGTH, "%s", name); + + mutex_lock(&vt_dev->session_lock); + session->disp_serial = rkvt_get_session_serial(&vt_dev->list_session, name); + snprintf(session->disp_name, RKVT_MAX_NAME_LENGTH, "%s-%d", + name, session->disp_serial); + + list_add_tail(&session->dev_link, &vt_dev->list_session); + + /* add debug fs */ + session->debug_root = debugfs_create_file(session->disp_name, + 0664, + vt_dev->debug_root, + session, + &debug_session_fops); + + mutex_unlock(&vt_dev->session_lock); + + rkvt_dbg(RKVT_DBG_USER, "vt session %s create\n", session->disp_name); + + return session; +} + +static void rkvt_session_destroy(struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + + rkvt_dbg(RKVT_DBG_USER, "vt session %s destroy\n", session->disp_name); + + mutex_lock(&vt_dev->inst_lock); + while ((inst = list_first_entry_or_null(&session->list_inst, + struct rkvt_instance, session_link))) { + list_del_init(&inst->session_link); + kref_put(&inst->ref, rkvt_inst_destroy); + } + mutex_unlock(&vt_dev->inst_lock); + + mutex_lock(&vt_dev->session_lock); + if (session->task) + put_task_struct(session->task); + list_del_init(&session->dev_link); + debugfs_remove_recursive(session->debug_root); + mutex_unlock(&vt_dev->session_lock); + + rkvt_inst_trim(session); + devm_kfree(vt_dev->dev, session); +} + +static int rkvt_open(struct inode *inode, struct file *filep) +{ + struct miscdevice *miscdev = filep->private_data; + struct rkvt_dev *vt_dev = container_of(miscdev, struct rkvt_dev, mdev); + struct rkvt_session *session; + char debug_name[64]; + + snprintf(debug_name, sizeof(debug_name), "%u", task_pid_nr(current->group_leader)); + session = rkvt_session_create(vt_dev, debug_name); + if (IS_ERR(session)) + return PTR_ERR(session); + + filep->private_data = session; + + return 0; +} + +static int rkvt_release(struct inode *inode, struct file *filep) +{ + struct rkvt_session *session = filep->private_data; + + rkvt_session_destroy(session); + filep->private_data = NULL; + + return 0; +} + +static int rkvt_get_connected_id(struct rkvt_dev *vt_dev) +{ + return atomic64_inc_return(&vt_dev->cid_generator); +} + +static struct rkvt_instance * +rkvt_inst_get_by_tid(struct rkvt_dev *vt_dev, int id) +{ + struct rkvt_instance *inst; + + mutex_lock(&vt_dev->inst_lock); + inst = idr_find(&vt_dev->inst_idr, id); + if (!inst) { + mutex_unlock(&vt_dev->inst_lock); + dev_err(vt_dev->dev, "find rkvt [%d] by device idr err, instance is null\n", id); + return NULL; + } + kref_get(&inst->ref); + mutex_unlock(&vt_dev->inst_lock); + + return inst; +} + +static void rkvt_inst_put(struct rkvt_instance *inst) +{ + struct rkvt_dev *vt_dev; + + if (!inst) + return; + + vt_dev = inst->vt_dev; + + mutex_lock(&vt_dev->inst_lock); + kref_put(&inst->ref, rkvt_inst_destroy); + mutex_unlock(&vt_dev->inst_lock); +} + +static int +rkvt_connect_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst; + int ret = 0; + + // ref get not put in function end, because connect need hold 1 refs. + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + + mutex_lock(&inst->lock); + if (data->caller == RKVT_CALLER_PRODUCER) { + if (inst->producer && inst->producer != session) { + dev_err(vt_dev->dev, "Connect to rkvt [%d] err, already has producer\n", + data->vt_id); + ret = -EINVAL; + goto connect_fail; + } + inst->producer = session; + } else if (data->caller == RKVT_CALLER_CONSUMER) { + if (inst->consumer && inst->consumer != session) { + dev_err(vt_dev->dev, "Connect to rkvt [%d] err, already has consumer\n", + data->vt_id); + ret = -EINVAL; + goto connect_fail; + } + inst->consumer = session; + } + mutex_unlock(&inst->lock); + session->cid = rkvt_get_connected_id(vt_dev); + session->caller = data->caller; + + rkvt_dbg(RKVT_DBG_USER, "rkvt [%d] %s-%d connect, instance ref %d\n", + inst->id, + data->caller == RKVT_CALLER_PRODUCER ? "producer" : "consumer", + session->pid, + kref_read(&inst->ref)); + + return 0; + +connect_fail: + mutex_unlock(&inst->lock); + // ref put for rkvt_instance_get_by_tid + rkvt_inst_put(inst); + + return ret; +} + +static int +rkvt_disconnect_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (session->caller != data->caller) + goto session_invail; + + mutex_lock(&inst->lock); + if (data->caller == RKVT_CALLER_PRODUCER) { + if (!inst->producer) + goto disconnect_fail; + if (inst->producer != session) + goto disconnect_fail; + + rkvt_session_trim_locked(session, inst); + inst->producer = NULL; + wake_up_interruptible(&inst->wait_producer); + } else if (data->caller == RKVT_CALLER_CONSUMER) { + if (!inst->consumer) + goto disconnect_fail; + if (inst->consumer != session) + goto disconnect_fail; + + rkvt_session_trim_locked(session, inst); + inst->consumer = NULL; + wake_up_interruptible(&inst->wait_consumer); + } + mutex_unlock(&inst->lock); + + rkvt_dbg(RKVT_DBG_USER, "rkvt [%d] %s-%d disconnect, instance ref %d\n", + inst->id, + data->caller == RKVT_CALLER_PRODUCER ? "producer" : "consumer", + session->pid, + kref_read(&inst->ref)); + // ref put for rkvt_instance_get_by_tid + rkvt_inst_put(inst); + // ref put for connect proc + rkvt_inst_put(inst); + session->cid = -1; + + return 0; + +disconnect_fail: + mutex_unlock(&inst->lock); +session_invail: + // ref put for rkvt_instance_get_by_tid + rkvt_inst_put(inst); + + return -EINVAL; +} + +static int +rkvt_reset_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst; + long long read_buf_id; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + + mutex_lock(&inst->lock); + rkvt_inst_clear_consumer(inst); + rkvt_inst_clear_producer(inst); + read_buf_id = atomic64_read(&vt_dev->buf_id_generator); + read_buf_id += 0x100; + read_buf_id &= ~0xff; + atomic64_set(&vt_dev->buf_id_generator, read_buf_id); + mutex_unlock(&inst->lock); + + rkvt_inst_put(inst); + + return 0; +} + +static int +rkvt_has_consumer_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + + mutex_lock(&inst->lock); + data->ctrl_data = inst->consumer != NULL ? 1 : 0; + mutex_unlock(&inst->lock); + + rkvt_inst_put(inst); + + return 0; +} + +static int +rkvt_ctrl_proc(struct rkvt_ctrl_data *data, struct rkvt_session *session) +{ + int id = data->vt_id; + int ret = 0; + + if (id < 0) + return -EINVAL; + if (data->caller == RKVT_CALLER_BUTT) + return -EINVAL; + + switch (data->ctrl_cmd) { + case RKVT_CTRL_CONNECT: { + ret = rkvt_connect_proc(data, session); + break; + } + case RKVT_CTRL_DISCONNECT: { + ret = rkvt_disconnect_proc(data, session); + break; + } + case RKVT_CTRL_RESET: { + ret = rkvt_reset_proc(data, session); + break; + } + case RKVT_CTRL_HAS_CONSUMER: { + ret = rkvt_has_consumer_proc(data, session); + break; + } + default: + pr_err("unknown rkvt cmd:%d\n", data->ctrl_cmd); + return -EINVAL; + } + + return ret; +} + +static struct +rkvt_buffer *rkvt_buf_get(struct rkvt_instance *inst, int key) +{ + struct rkvt_buffer *buffer = NULL; + int i; + + mutex_lock(&inst->lock); + for (i = 0; i < RKVT_POOL_SIZE; i++) { + buffer = &inst->vt_buffers[i]; + + if (buffer->base.buf_status == RKVT_BUF_ACQUIRE && + buffer->fds_con[0] == key) + break; + } + mutex_unlock(&inst->lock); + + return buffer; +} + +static int +rkvt_has_buf(struct rkvt_instance *inst, enum rkvt_caller_e caller) +{ + int ret = 0; + + if (caller == RKVT_CALLER_PRODUCER) + ret = !kfifo_is_empty(&inst->fifo_to_producer); + else + ret = !kfifo_is_empty(&inst->fifo_to_consumer); + + return ret; +} + +static int +rkvt_query_buf_and_wait(struct rkvt_instance *inst, + enum rkvt_caller_e caller, + int timeout_ms) +{ + int ret; + wait_queue_head_t *wait_queue; + + if (caller == RKVT_CALLER_PRODUCER) + wait_queue = &inst->wait_producer; + else + wait_queue = &inst->wait_consumer; + if (caller == RKVT_CALLER_PRODUCER && + !kfifo_is_empty(&inst->fifo_to_producer)) + return 0; + if (caller == RKVT_CALLER_CONSUMER && + !kfifo_is_empty(&inst->fifo_to_consumer)) + return 0; + + if (timeout_ms < 0) + wait_event_interruptible(*wait_queue, + rkvt_has_buf(inst, caller)); + else if (timeout_ms > 0) { + ret = wait_event_interruptible_timeout(*wait_queue, + rkvt_has_buf(inst, caller), + msecs_to_jiffies(timeout_ms)); + /* timeout */ + if (ret == 0) + return -EAGAIN; + } else + return -EAGAIN; + + if (caller == RKVT_CALLER_PRODUCER && + kfifo_is_empty(&inst->fifo_to_producer)) + return -EAGAIN; + if (caller == RKVT_CALLER_CONSUMER && + kfifo_is_empty(&inst->fifo_to_consumer)) + return -EAGAIN; + + return 0; +} + +static struct rkvt_buffer *rkvt_get_free_buf(struct rkvt_instance *inst) +{ + struct rkvt_buffer *buffer = NULL; + int i, status; + + mutex_lock(&inst->lock); + for (i = 0; i < RKVT_POOL_SIZE; i++) { + status = inst->vt_buffers[i].base.buf_status; + if (status == RKVT_BUF_FREE || status == RKVT_BUF_DEQUEUE) { + buffer = &inst->vt_buffers[i]; + memset(buffer->file_buf, 0, sizeof(buffer->file_buf)); + buffer->rendered_fence = NULL; + break; + } + } + mutex_unlock(&inst->lock); + + return buffer; +} + +static int +rkvt_queue_buf(struct rkvt_buf_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + struct rkvt_buf_base *base = NULL; + struct rkvt_buffer *buffer = NULL; + int i; + int ret = 0; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (!inst->producer || inst->producer != session) { + ret = -EINVAL; + goto queue_fail; + } + if ((data->base.num_fds > MAX_BUF_HANDLE_FDS) || + (data->base.num_ints > MAX_BUF_HANDLE_INTS)) { + ret = -EINVAL; + goto queue_fail; + } + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTQB [%d] start\n", inst->id); + + base = &data->base; + buffer = rkvt_get_free_buf(inst); + for (i = 0; i < base->num_fds; i++) { + buffer->fds_con[i] = -1; + buffer->fds_pro[i] = base->fds[i]; + buffer->file_buf[i] = fget(base->fds[i]); + + if (!buffer->file_buf[i]) { + ret = -EBADF; + goto buf_fget_fail; + } + + inst->fcount++; + rkvt_dbg(RKVT_DBG_FILE, + "VTQB [%d] fget file(%p) buf(%p) buf session(%p) ino(%08lu) fcount=%d\n", + inst->id, buffer->file_buf[i], buffer, buffer->session_pro, + buffer->file_buf[i] ? file_inode(buffer->file_buf[i])->i_ino : 0, + inst->fcount); + } + + if (base->fence_fd >= 0) + buffer->ready_render_fence = fget(base->fence_fd); + + // buffer id is empty, generate a new id + if (base->buffer_id == 0) + base->buffer_id = atomic64_inc_return(&vt_dev->buf_id_generator); + buffer->base = *base; + buffer->base.buf_status = RKVT_BUF_QUEUE; + buffer->session_pro = session; + buffer->cid_pro = session->cid; + + mutex_lock(&inst->lock); + if (inst->consumer) { + kfifo_put(&inst->fifo_to_consumer, buffer); + } else { + for (i = 0; i < buffer->base.num_fds; i++) { + if (buffer->file_buf[i]) { + fput(buffer->file_buf[i]); + buffer->file_buf[i] = NULL; + } + inst->fcount--; + } + if (buffer->ready_render_fence) { + fput(buffer->ready_render_fence); + buffer->ready_render_fence = NULL; + } + buffer->base.buf_status = RKVT_BUF_RELEASE; + kfifo_put(&inst->fifo_to_producer, buffer); + } + mutex_unlock(&inst->lock); + + if (inst->consumer) + wake_up_interruptible(&inst->wait_consumer); + else if (inst->producer) + wake_up_interruptible(&inst->wait_producer); + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTQB [%d] pfd[0]:%d end\n", inst->id, buffer->fds_pro[0]); + +queue_fail: + rkvt_inst_put(inst); + + return ret; +buf_fget_fail: + for (i = 0; i < base->num_fds; i++) { + if (buffer->file_buf[i]) { + fput(buffer->file_buf[i]); + buffer->file_buf[i] = NULL; + inst->fcount--; + } + } + rkvt_inst_put(inst); + + return ret; +} + +static int +rkvt_deque_buf(struct rkvt_buf_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + struct rkvt_buffer *buffer = NULL; + int ret = 0; + unsigned long long cur_time, wait_time; + int i; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (!inst->producer || inst->producer != session) { + ret = -EINVAL; + goto deque_fail; + } + + /* empty need wait */ + ret = rkvt_query_buf_and_wait(inst, + RKVT_CALLER_PRODUCER, + data->timeout_ms); + if (ret) + goto deque_fail; + + mutex_lock(&inst->lock); + ret = kfifo_get(&inst->fifo_to_producer, &buffer); + if (!ret || !buffer) { + dev_err(vt_dev->dev, "VTDB [%d] got null buffer ret(%d)\n", inst->id, ret); + mutex_unlock(&inst->lock); + ret = -EAGAIN; + goto deque_fail; + } + mutex_unlock(&inst->lock); + + /* it's previous connect buffer */ + if (buffer->cid_pro != session->cid) { + if (buffer->rendered_fence) { + dma_fence_put(buffer->rendered_fence); + buffer->rendered_fence = NULL; + } + + ret = -EAGAIN; + goto deque_fail; + } + + if (buffer->rendered_fence) { + cur_time = sched_clock(); + ret = dma_fence_wait_timeout(buffer->rendered_fence, false, + msecs_to_jiffies(RKVT_FENCE_WAIT_MS)); + wait_time = sched_clock() - cur_time; + rkvt_dbg(RKVT_DBG_BUFFERS, + "VTDB [%d] pfd[0]:%d rendered fence:%p fence_wait time %llu\n", + inst->id, buffer->fds_pro[0], buffer->rendered_fence, wait_time); + + if (ret < 0) + dev_err(vt_dev->dev, "VTDB [%d] wait fence timeout\n", inst->id); + + dma_fence_put(buffer->rendered_fence); + buffer->rendered_fence = NULL; + } + for (i = 0; i < buffer->base.num_fds; i++) + rkvt_dbg(RKVT_DBG_FILE, + "VTDB [%d] fget file(%p) buf(%p) buf session(%p) ino(%08lu) fcount=%d\n", + inst->id, buffer->file_buf[i], + buffer, buffer->session_pro, + buffer->file_buf[i] ? file_inode(buffer->file_buf[i])->i_ino : 0, + inst->fcount); + + buffer->base.vt_id = inst->id; + /* return the buffer */ + data->base = buffer->base; + buffer->base.buf_status = RKVT_BUF_DEQUEUE; + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTDB [%d] end pfd[0]:%d\n", inst->id, buffer->fds_pro[0]); + +deque_fail: + rkvt_inst_put(inst); + + return ret; +} + +static int +rkvt_acquire_buf(struct rkvt_buf_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + struct rkvt_buffer *buffer = NULL; + int fd, ret = -1; + int i; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (!inst->consumer || inst->consumer != session) { + ret = -EINVAL; + goto acquire_fail; + } + if ((data->base.num_fds > MAX_BUF_HANDLE_FDS) || + (data->base.num_ints > MAX_BUF_HANDLE_INTS)) { + ret = -EINVAL; + goto acquire_fail; + } + + /* empty need wait */ + ret = rkvt_query_buf_and_wait(inst, + RKVT_CALLER_CONSUMER, + data->timeout_ms); + if (ret) + goto acquire_fail; + + mutex_lock(&inst->lock); + ret = kfifo_get(&inst->fifo_to_consumer, &buffer); + mutex_unlock(&inst->lock); + if (!ret || !buffer) { + dev_err(vt_dev->dev, "VTAB [%d] got null buffer\n", inst->id); + ret = -EAGAIN; + goto acquire_fail; + } + + /* get the fd in consumer */ + for (i = 0; i < buffer->base.num_fds; i++) { + if (buffer->fds_con[i] <= 0) { + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + goto no_memory; + + fd_install(fd, buffer->file_buf[i]); + buffer->fds_con[i] = fd; + buffer->base.fds[i] = fd; + } + } + if (buffer->ready_render_fence) { + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + goto no_memory; + fd_install(fd, buffer->ready_render_fence); + buffer->base.fence_fd = fd; + } + buffer->base.vt_id = inst->id; + data->base = buffer->base; + buffer->base.buf_status = RKVT_BUF_ACQUIRE; + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTAB [%d] pfd[0](%d) buf(%p) buf session(%p)\n", + inst->id, buffer->fds_pro[0], buffer, buffer->session_pro); + + rkvt_inst_put(inst); + + return 0; + +no_memory: + pr_info("VTAB [%d] install fd error\n", inst->id); + mutex_lock(&inst->lock); + for (i = 0; i < buffer->base.num_fds; i++) { + rkvt_dbg(RKVT_DBG_FILE, + "VTAB [%d] install fd error file(%p) buf(%p) ino(%08lu) fcount=%d\n", + inst->id, buffer->file_buf[i], buffer, + file_inode(buffer->file_buf[i])->i_ino, inst->fcount); + if (buffer->file_buf[i]) { + fput(buffer->file_buf[i]); + buffer->file_buf[i] = NULL; + inst->fcount--; + } + } + if (buffer->ready_render_fence) { + fput(buffer->ready_render_fence); + buffer->ready_render_fence = NULL; + } + buffer->base.buf_status = RKVT_BUF_RELEASE; + + kfifo_put(&inst->fifo_to_producer, buffer); + mutex_unlock(&inst->lock); + if (inst->producer) + wake_up_interruptible(&inst->wait_producer); + ret = -ENOMEM; + +acquire_fail: + rkvt_inst_put(inst); + + return ret; +} + +static int +rkvt_release_buf(struct rkvt_buf_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + struct rkvt_buf_base *buf_base = NULL; + struct rkvt_buffer *buffer = NULL; + int i; + int ret = 0; + long long read_buf_id; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (!inst->consumer || inst->consumer != session) { + ret = -EINVAL; + goto release_fail; + } + + buf_base = &data->base; + buffer = rkvt_buf_get(inst, buf_base->fds[0]); + if (!buffer) { + ret = -EINVAL; + goto release_fail; + } + + if (buf_base->fence_fd >= 0) + buffer->rendered_fence = sync_file_get_fence(buf_base->fence_fd); + + if (!buffer->rendered_fence) + rkvt_dbg(RKVT_DBG_BUFFERS, "VTRB [%d] rendered fence file is null\n", inst->id); + + /* close the fds in consumer side */ + for (i = 0; i < buf_base->num_fds; i++) { + rkvt_dbg(RKVT_DBG_FILE, + "VTRB [%d] file(%p) buf(%p) buf session(%p) ino(%08lu) fcount=%d\n", + inst->id, buffer->file_buf[i], buffer, buffer->session_pro, + buffer->file_buf[i] ? file_inode(buffer->file_buf[i])->i_ino : 0, + inst->fcount); + rkvt_close_fd(session, buffer->fds_con[i]); + inst->fcount--; + buffer->base.fds[i] = buffer->fds_pro[i]; + } + if (buffer->ready_render_fence) { + fput(buffer->ready_render_fence); + buffer->ready_render_fence = NULL; + } + + buffer->base.crop = buf_base->crop; + buffer->base.buf_status = RKVT_BUF_RELEASE; + + mutex_lock(&inst->lock); + read_buf_id = atomic64_read(&vt_dev->buf_id_generator); + /* if producer has disconnect */ + if (!inst->producer) { + rkvt_dbg(RKVT_DBG_BUFFERS, "VTRB [%d], buffer no producer\n", inst->id); + buffer->base.buf_status = RKVT_BUF_FREE; + } else if ((buffer->base.buffer_id >> 8) != (read_buf_id >> 8)) { + dev_err(vt_dev->dev, "VTRB [%d] generation is different. cur(%lld) VS exp(%lld)\n", + inst->id, buffer->base.buffer_id >> 8, read_buf_id >> 8); + buffer->base.buf_status = RKVT_BUF_FREE; + } else { + if (buffer->session_pro && + buffer->session_pro != inst->producer) { + rkvt_dbg(RKVT_DBG_BUFFERS, + "VTRB [%d] producer not valid, producer(%p), buf session(%p)\n", + inst->id, inst->producer, buffer->session_pro); + buffer->base.buf_status = RKVT_BUF_FREE; + } + + kfifo_put(&inst->fifo_to_producer, buffer); + } + mutex_unlock(&inst->lock); + + if (inst->producer) + wake_up_interruptible(&inst->wait_producer); + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTRB [%d] pfd[0]:%d end\n", inst->id, buffer->fds_pro[0]); + +release_fail: + rkvt_inst_put(inst); + + return ret; +} + +static int +rkvt_cancel_buf(struct rkvt_buf_data *data, struct rkvt_session *session) +{ + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + struct rkvt_buf_base *buf_base = NULL; + struct rkvt_buffer *buffer = NULL; + int i; + + inst = rkvt_inst_get_by_tid(vt_dev, data->vt_id); + if (!inst) + return -EINVAL; + if (!inst->producer || inst->producer != session) { + rkvt_inst_put(inst); + return -EINVAL; + } + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTCB [%d] start\n", inst->id); + + buf_base = &data->base; + buffer = rkvt_get_free_buf(inst); + for (i = 0; i < buf_base->num_fds; i++) { + buffer->fds_con[i] = -1; + buffer->fds_pro[i] = buf_base->fds[i]; + rkvt_dbg(RKVT_DBG_FILE, + "VTCB [%d] fget file(%p) buf(%p) buf session(%p) fcount=%d\n", + inst->id, buffer->file_buf[i], buffer, + buffer->session_pro, inst->fcount); + } + // buffer id is empty, generate a new id + if (buf_base->buffer_id == 0) + buf_base->buffer_id = atomic64_inc_return(&vt_dev->buf_id_generator); + buffer->base = *buf_base; + buffer->base.buf_status = RKVT_BUF_RELEASE; + buffer->session_pro = session; + buffer->cid_pro = session->cid; + + mutex_lock(&inst->lock); + kfifo_put(&inst->fifo_to_producer, buffer); + mutex_unlock(&inst->lock); + + if (inst->producer) + wake_up_interruptible(&inst->wait_producer); + + rkvt_dbg(RKVT_DBG_BUFFERS, "VTCB [%d] pfd[0]:%d end\n", inst->id, buffer->fds_pro[0]); + rkvt_inst_put(inst); + + return 0; +} + +static unsigned int rkvt_ioctl_dir(unsigned int cmd) +{ + switch (cmd) { + case RKVT_IOC_ALLOC_ID: + case RKVT_IOC_DEQUE_BUF: + case RKVT_IOC_ACQUIRE_BUF: + case RKVT_IOC_CTRL: + return _IOC_READ; + case RKVT_IOC_QUEUE_BUF: + case RKVT_IOC_RELEASE_BUF: + case RKVT_IOC_CANCEL_BUF: + case RKVT_IOC_FREE_ID: + return _IOC_WRITE; + default: + return _IOC_DIR(cmd); + } +} + +static long rkvt_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + union rkvt_ioc_arg data; + struct rkvt_session *session = filep->private_data; + unsigned int dir = rkvt_ioctl_dir(cmd); + struct rkvt_dev *vt_dev = session->vt_dev; + struct rkvt_instance *inst = NULL; + + rkvt_dbg(RKVT_DBG_CMD, "rkvt ioctl cmd 0x%x size %d in\n", cmd, _IOC_SIZE(cmd)); + + if (_IOC_SIZE(cmd) > sizeof(data)) + return -EINVAL; + + if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) + return -EFAULT; + + switch (cmd) { + case RKVT_IOC_ALLOC_ID: { + char name[64]; + + inst = rkvt_inst_create(session->vt_dev); + if (IS_ERR(inst)) + return PTR_ERR(inst); + + mutex_lock(&vt_dev->inst_lock); + ++vt_dev->inst_id_generator; + ret = idr_alloc(&vt_dev->inst_idr, inst, + vt_dev->inst_id_generator, 0, GFP_KERNEL); + mutex_unlock(&vt_dev->inst_lock); + if (ret < 0) { + rkvt_inst_put(inst); + return ret; + } + + inst->id = ret; + snprintf(name, sizeof(name), "instance-%d", inst->id); + inst->debug_root = + debugfs_create_file(name, 0664, vt_dev->debug_root, + inst, &dbg_instance_fops); + + mutex_lock(&vt_dev->inst_lock); + list_add_tail(&inst->session_link, &session->list_inst); + mutex_unlock(&vt_dev->inst_lock); + + data.alloc_data.vt_id = inst->id; + rkvt_dbg(RKVT_DBG_USER, "rkvt alloc instance [%d], ref %d\n", + inst->id, kref_read(&inst->ref)); + break; + } + case RKVT_IOC_FREE_ID: { + inst = rkvt_inst_get_by_tid(vt_dev, data.alloc_data.vt_id); + /* to do free id operation check */ + if (!inst) { + dev_err(vt_dev->dev, "destroy unknown videotunnel instance:%d\n", + data.alloc_data.vt_id); + ret = -EINVAL; + } else { + rkvt_dbg(RKVT_DBG_USER, "rkvt free instance [%d], ref %d\n", + inst->id, kref_read(&inst->ref)); + + mutex_lock(&vt_dev->inst_lock); + list_del_init(&inst->session_link); + mutex_unlock(&vt_dev->inst_lock); + // ref put for rkvt_instance_get_by_tid + rkvt_inst_put(inst); + // ref put for kref_init in rkvt_inst_create + rkvt_inst_put(inst); + } + break; + } + case RKVT_IOC_CTRL: + ret = rkvt_ctrl_proc(&data.ctrl_data, session); + break; + case RKVT_IOC_QUEUE_BUF: + ret = rkvt_queue_buf(&data.buffer_data, session); + break; + case RKVT_IOC_DEQUE_BUF: + ret = rkvt_deque_buf(&data.buffer_data, session); + break; + case RKVT_IOC_RELEASE_BUF: + ret = rkvt_release_buf(&data.buffer_data, session); + break; + case RKVT_IOC_ACQUIRE_BUF: + ret = rkvt_acquire_buf(&data.buffer_data, session); + break; + case RKVT_IOC_CANCEL_BUF: + ret = rkvt_cancel_buf(&data.buffer_data, session); + break; + default: + dev_err(vt_dev->dev, "%s: cmd 0x%x not found.\n", __func__, cmd); + return -ENOTTY; + } + + if (dir & _IOC_READ) { + if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) + return -EFAULT; + } + + return ret; +} + +static const struct file_operations vt_fops = { + .owner = THIS_MODULE, + .open = rkvt_open, + .release = rkvt_release, + .unlocked_ioctl = rkvt_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rkvt_ioctl, +#endif +}; + +static int rkvt_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev = &pdev->dev; + struct rkvt_dev *vdev = NULL; + + dev_info(dev, "probe start\n"); + vdev = devm_kzalloc(dev, sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + + vdev->dev = dev; + vdev->dev_name = DEVICE_NAME; + vdev->mdev.minor = MISC_DYNAMIC_MINOR; + vdev->mdev.name = DEVICE_NAME; + vdev->mdev.fops = &vt_fops; + platform_set_drvdata(pdev, vdev); + + ret = misc_register(&vdev->mdev); + if (ret) { + dev_err(dev, "misc_register fail.\n"); + return ret; + } + + mutex_init(&vdev->inst_lock); + mutex_init(&vdev->session_lock); + idr_init(&vdev->inst_idr); + atomic64_set(&vdev->cid_generator, 0); + INIT_LIST_HEAD(&vdev->list_inst); + INIT_LIST_HEAD(&vdev->list_session); + vdev->debug_root = debugfs_create_dir(DEVICE_NAME, NULL); + if (!vdev->debug_root) + dev_err(dev, "failed to create debugfs root directory.\n"); + + dev_info(dev, "probe success\n"); + + return 0; +} + +static int rkvt_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rkvt_dev *vdev = platform_get_drvdata(pdev); + + dev_info(dev, "remove device\n"); + + idr_destroy(&vdev->inst_idr); + debugfs_remove_recursive(vdev->debug_root); + misc_deregister(&vdev->mdev); + + return 0; +} + +static const struct of_device_id rk_vt_match[] = { + { + .compatible = "rockchip,video-tunnel", + }, + { }, +}; + +static struct platform_driver rk_vt_driver = { + .probe = rkvt_probe, + .remove = rkvt_remove, + .driver = { + .name = "rk_videotunnel_driver", + .owner = THIS_MODULE, + .of_match_table = rk_vt_match, + }, +}; + +module_platform_driver(rk_vt_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ROCKCHIP videotunnel driver"); diff --git a/drivers/video/rockchip/vtunnel/rkvtunnel.h b/drivers/video/rockchip/vtunnel/rkvtunnel.h new file mode 100644 index 000000000000..1781aa785bc0 --- /dev/null +++ b/drivers/video/rockchip/vtunnel/rkvtunnel.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd + */ +#ifndef __ROCKCHIP_VIDEO_TUNNEL_H__ +#define __ROCKCHIP_VIDEO_TUNNEL_H__ + +#include +#include + +#define MAX_BUF_HANDLE_FDS 16 +#define MAX_BUF_HANDLE_INTS 128 + +#define RKVT_IOC_MAGIC 'V' +#define RKVT_IOWR(nr, type) _IOWR(RKVT_IOC_MAGIC, nr, type) + +#define RKVT_IOC_ALLOC_ID RKVT_IOWR(0x0, struct rkvt_alloc_id_data) +#define RKVT_IOC_FREE_ID RKVT_IOWR(0x1, struct rkvt_alloc_id_data) +#define RKVT_IOC_CTRL RKVT_IOWR(0x2, struct rkvt_ctrl_data) +#define RKVT_IOC_QUEUE_BUF RKVT_IOWR(0x3, struct rkvt_buf_data) +#define RKVT_IOC_DEQUE_BUF RKVT_IOWR(0x4, struct rkvt_buf_data) +#define RKVT_IOC_CANCEL_BUF RKVT_IOWR(0x5, struct rkvt_buf_data) +#define RKVT_IOC_ACQUIRE_BUF RKVT_IOWR(0x6, struct rkvt_buf_data) +#define RKVT_IOC_RELEASE_BUF RKVT_IOWR(0x7, struct rkvt_buf_data) + +// caller type +enum rkvt_caller_e { + RKVT_CALLER_PRODUCER, + RKVT_CALLER_CONSUMER, + RKVT_CALLER_BUTT, +}; + +// video tunnel caller control +enum rkvt_ctrl_cmd_e { + RKVT_CTRL_CONNECT, + RKVT_CTRL_DISCONNECT, + RKVT_CTRL_RESET, + RKVT_CTRL_HAS_CONSUMER, + RKVT_CTRL_BUTT, +}; + +struct rkvt_alloc_id_data { + int vt_id; +}; + +struct rkvt_ctrl_data { + int vt_id; + enum rkvt_caller_e caller; + enum rkvt_ctrl_cmd_e ctrl_cmd; + int ctrl_data; +}; + +struct rkvt_rect { + int left; + int top; + int right; + int bottom; +}; + +struct rkvt_buf_base { + int vt_id; + int fence_fd; + int buf_status; + int num_fds; /* number of file-descriptors at &data[0] */ + int num_ints; /* number of ints at &data[numFds] */ + int reserved; + int fds[MAX_BUF_HANDLE_FDS]; + int ints[MAX_BUF_HANDLE_INTS]; + int64_t priv_data; + uint64_t expected_present_time; + uint64_t buffer_id; + struct rkvt_rect crop; +}; + +struct rkvt_buf_data { + int vt_id; + int timeout_ms; /* 0: non block, negative: block, other: timeout ms */ + struct rkvt_buf_base base; +}; + +#endif diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 9f0d4d25ed2a..8ffa0c14b8d8 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -145,6 +145,7 @@ struct data_chunk { * Otherwise, destination is filled contiguously (icg ignored). * Ignored if dst_inc is false. * @numf: Number of frames in this template. + * @nump: Number of period frames in this template. * @frame_size: Number of chunks in a frame i.e, size of sgl[]. * @sgl: Array of {chunk,icg} pairs that make up a frame. */ @@ -157,6 +158,9 @@ struct dma_interleaved_template { bool src_sgl; bool dst_sgl; size_t numf; +#ifdef CONFIG_NO_GKI + size_t nump; +#endif size_t frame_size; struct data_chunk sgl[]; }; @@ -448,10 +452,6 @@ struct dma_slave_config { bool device_fc; void *peripheral_config; size_t peripheral_size; -#ifdef CONFIG_NO_GKI - unsigned int src_interlace_size; - unsigned int dst_interlace_size; -#endif }; /** diff --git a/include/linux/icm4260x.h b/include/linux/icm4260x.h new file mode 100644 index 000000000000..8917d8bc307e --- /dev/null +++ b/include/linux/icm4260x.h @@ -0,0 +1,432 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Definitions for icm4260x chip. + */ +#ifndef __ICM4260X_ACC_H +#define __ICM4260X_ACC_H + +/* Registers and associated bit definitions */ +/* Bank 0 */ +#define ICM4260X_MISC_1 0x00 +#define ICM4260X_CHIP_CONFIG_REG 0x01 +#define ICM4260X_SIGNAL_PATH_RESET 0x02 +#define ICM4260X_DRIVE_CONFIG_REG1 0x03 +#define ICM4260X_DRIVE_CONFIG_REG2 0x04 +#define ICM4260X_DRIVE_CONFIG_REG3 0x05 +#define ICM4260X_INT_CONFIG_REG 0x06 +#define ICM4260X_ODRGRID0 0x07 +#define ICM4260X_ODRGRID1 0x08 +#define ICM4260X_TEMP_DATA0 0x09 +#define ICM4260X_TEMP_DATA1 0x0a +#define ICM4260X_ACCEL_DATA_X0 0x0b +#define ICM4260X_ACCEL_DATA_X1 0x0c +#define ICM4260X_ACCEL_DATA_Y0 0x0d +#define ICM4260X_ACCEL_DATA_Y1 0x0e +#define ICM4260X_ACCEL_DATA_Z0 0x0f +#define ICM4260X_ACCEL_DATA_Z1 0x10 +#define ICM4260X_GYRO_DATA_X0 0x11 +#define ICM4260X_GYRO_DATA_X1 0x12 +#define ICM4260X_GYRO_DATA_Y0 0x13 +#define ICM4260X_GYRO_DATA_Y1 0x14 +#define ICM4260X_GYRO_DATA_Z0 0x15 +#define ICM4260X_GYRO_DATA_Z1 0x16 +#define ICM4260X_TMST_FSYNC1 0x17 +#define ICM4260X_TMST_FSYNC2 0x18 +#define ICM4260X_ODR_LP_STATUS 0x19 +#define ICM4260X_PWR_MGMT_0 0x1f +#define ICM4260X_GYRO_CONFIG0 0x20 +#define ICM4260X_ACCEL_CONFIG0 0x21 +#define ICM4260X_TEMP_CONFIG0 0x22 +#define ICM4260X_GYRO_CONFIG1 0x23 +#define ICM4260X_ACCEL_CONFIG1 0x24 +#define ICM4260X_APEX_CONFIG0 0x25 +#define ICM4260X_APEX_CONFIG1 0x26 +#define ICM4260X_WOM_CONFIG 0x27 +#define ICM4260X_FIFO_CONFIG1 0x28 +#define ICM4260X_FIFO_CONFIG2 0x29 +#define ICM4260X_FIFO_CONFIG3 0x2a +#define ICM4260X_INT_SOURCE0 0x2b +#define ICM4260X_INT_SOURCE1 0x2c +#define ICM4260X_INT_SOURCE3 0x2d +#define ICM4260X_INT_SOURCE4 0x2e +#define ICM4260X_FIFO_LOST_PKT0 0x2f +#define ICM4260X_FIFO_LOST_PKT1 0x30 +#define ICM4260X_APEX_DATA0 0x31 +#define ICM4260X_APEX_DATA1 0x32 +#define ICM4260X_APEX_DATA2 0x33 +#define ICM4260X_APEX_DATA3 0x34 +#define ICM4260X_INTF_CONFIG0 0x35 +#define ICM4260X_INTF_CONFIG1 0x36 +#define ICM4260X_INT_STATUS_DRDY 0x39 +#define ICM4260X_INT_STATUS 0x3a +#define ICM4260X_INT_STATUS2 0x3b +#define ICM4260X_INT_STATUS3 0x3c +#define ICM4260X_FIFO_BYTE_COUNT1 0x3d +#define ICM4260X_FIFO_BYTE_COUNT2 0x3e +#define ICM4260X_FIFO_DATA_REG 0x3f +#define ICM4260X_S4S_GYRO_TPH1 0x40 +#define ICM4260X_S4S_GYRO_TPH2 0x41 +#define ICM4260X_S4S_ACCEL_TPH1 0x42 +#define ICM4260X_S4S_ACCEL_TPH2 0x43 +#define ICM4260X_S4S_RR 0x44 +#define ICM4260X_GYR_BIAS_CFG1 0x46 +#define ICM4260X_WHO_AM_I 0x75 +#define ICM4260X_S4S_ST 0x76 +#define ICM4260X_S4S_ST_CLONE 0x77 +#define ICM4260X_S4S_DT 0x78 +#define ICM4260X_BLK_SEL_W 0x79 +#define ICM4260X_MADDR_W 0x7a +#define ICM4260X_M_W 0x7b +#define ICM4260X_BLK_SEL_R 0x7c +#define ICM4260X_MADDR_R 0x7d +#define ICM4260X_M_R 0x7e + +/* MREG_TOP1 */ +#define ICM4260X_TMST_CONFIG1_MREG_TOP1 0x00 +#define ICM4260X_FIFO_CONFIG5_MREG_TOP1 0x01 +#define ICM4260X_FIFO_CONFIG6_MREG_TOP1 0x02 +#define ICM4260X_FSYNC_CONFIG_MREG_TOP1 0x03 +#define ICM4260X_INT_CONFIG0_MREG_TOP1 0x04 +#define ICM4260X_INT_CONFIG1_MREG_TOP1 0x05 +#define ICM4260X_AFSR_CONFIG0_MREG_TOP1 0x07 +#define ICM4260X_AFSR_CONFIG1_MREG_TOP1 0x08 +#define ICM4260X_TBC_RCOSC_MREG_TOP1 0x0d +#define ICM4260X_TBC_PLL_MREG_TOP1 0x0e +#define ICM4260X_ST_CONFIG_MREG_TOP1 0x13 +#define ICM4260X_SELFTEST_MREG_TOP1 0x14 +#define ICM4260X_PADS_CONFIG3_MREG_TOP1 0x17 +#define ICM4260X_TEMP_CONFIG1_MREG_TOP1 0x1c +#define ICM4260X_TEMP_CONFIG3_MREG_TOP1 0x1e +#define ICM4260X_S4S_CONFIG1_MREG_TOP1 0x1f +#define ICM4260X_S4S_CONFIG2_MREG_TOP1 0x20 +#define ICM4260X_S4S_FREQ_RATIO1_MREG_TOP1 0x21 +#define ICM4260X_S4S_FREQ_RATIO2_MREG_TOP1 0x22 +#define ICM4260X_INTF_CONFIG6_MREG_TOP1 0x23 +#define ICM4260X_INTF_CONFIG10_MREG_TOP1 0x25 +#define ICM4260X_INTF_CONFIG7_MREG_TOP1 0x28 +#define ICM4260X_OTP_CONFIG_MREG_TOP1 0x2b +#define ICM4260X_INT_SOURCE6_MREG_TOP1 0x2f +#define ICM4260X_INT_SOURCE7_MREG_TOP1 0x30 +#define ICM4260X_INT_SOURCE8_MREG_TOP1 0x31 +#define ICM4260X_INT_SOURCE9_MREG_TOP1 0x32 +#define ICM4260X_INT_SOURCE10_MREG_TOP1 0x33 +#define ICM4260X_GYRO_PWR_CFG0_MREG_TOP1 0x38 +#define ICM4260X_ACCEL_CP_CFG0_MREG_TOP1 0x39 +#define ICM4260X_APEX_CONFIG2_MREG_TOP1 0x44 +#define ICM4260X_APEX_CONFIG3_MREG_TOP1 0x45 +#define ICM4260X_APEX_CONFIG4_MREG_TOP1 0x46 +#define ICM4260X_APEX_CONFIG5_MREG_TOP1 0x47 +#define ICM4260X_APEX_CONFIG9_MREG_TOP1 0x48 +#define ICM4260X_APEX_CONFIG10_MREG_TOP1 0x49 +#define ICM4260X_APEX_CONFIG11_MREG_TOP1 0x4a +#define ICM4260X_APEX_CONFIG12_MREG_TOP1 0x67 +#define ICM4260X_ACCEL_WOM_X_THR_MREG_TOP1 0x4b +#define ICM4260X_ACCEL_WOM_Y_THR_MREG_TOP1 0x4c +#define ICM4260X_ACCEL_WOM_Z_THR_MREG_TOP1 0x4d +#define ICM4260X_GOS_USER0_MREG_TOP1 0x4e +#define ICM4260X_GOS_USER1_MREG_TOP1 0x4f +#define ICM4260X_GOS_USER2_MREG_TOP1 0x50 +#define ICM4260X_GOS_USER3_MREG_TOP1 0x51 +#define ICM4260X_GOS_USER4_MREG_TOP1 0x52 +#define ICM4260X_GOS_USER5_MREG_TOP1 0x53 +#define ICM4260X_GOS_USER6_MREG_TOP1 0x54 +#define ICM4260X_GOS_USER7_MREG_TOP1 0x55 +#define ICM4260X_GOS_USER8_MREG_TOP1 0x56 +#define ICM4260X_ST_STATUS1_MREG_TOP1 0x63 +#define ICM4260X_ST_STATUS2_MREG_TOP1 0x64 + +/* MMEM_TOP */ +#define ICM4260X_XA_ST_DATA_MMEM_TOP 0x5000 +#define ICM4260X_YA_ST_DATA_MMEM_TOP 0x5001 +#define ICM4260X_ZA_ST_DATA_MMEM_TOP 0x5002 +#define ICM4260X_XG_ST_DATA_MMEM_TOP 0x5003 +#define ICM4260X_YG_ST_DATA_MMEM_TOP 0x5004 +#define ICM4260X_ZG_ST_DATA_MMEM_TOP 0x5005 + +/* MREG_OTP */ +#define ICM4260X_OTP_CTRL7_MREG_OTP 0x2806 + +/* Bank0 REG_GYRO_CONFIG0/REG_ACCEL_CONFIG0 */ +#define SHIFT_GYRO_FS_SEL 5 +#define SHIFT_ACCEL_FS_SEL 5 +#define SHIFT_ODR_CONF 0 +#define BIT_GYRO_FSR 0x60 +#define BIT_GYRO_ODR 0x0F +#define BIT_ACCEL_FSR 0x60 +#define ACCEL_FS_SEL 3 //(-2G, +2G) +#define GYRO_FS_SEL 3 //00~11:2000dps\1000dps\500dps\250dps +#define BIT_ACCEL_ODR 0x0F +#define BIT_SENSOR_ODR_800HZ 0x06 +#define BIT_SENSOR_ODR_400HZ 0x07 +#define BIT_SENSOR_ODR_200HZ 0x08 +#define BIT_SENSOR_ODR_100HZ 0x09 +#define BIT_SENSOR_ODR_50HZ 0x0A +#define BIT_SENSOR_ODR_25HZ 0x0B +#define BIT_SENSOR_ODR_12HZ 0x0C +#define BIT_SENSOR_ODR_6HZ 0x0D +#define BIT_SENSOR_ODR_3HZ 0x0E + +/* Bank0 REG_GYRO_CONFIG1 */ +#define BIT_GYR_UI_FLT_BW_BYPASS 0x00 +#define BIT_GYR_UI_FLT_BW_180HZ 0x01 +#define BIT_GYR_UI_FLT_BW_121HZ 0x02 +#define BIT_GYR_UI_FLT_BW_73HZ 0x03 +#define BIT_GYR_UI_FLT_BW_53HZ 0x04 +#define BIT_GYR_UI_FLT_BW_34HZ 0x05 +#define BIT_GYR_UI_FLT_BW_25HZ 0x06 +#define BIT_GYR_UI_FLT_BW_16HZ 0x07 +#define BIT_GYR_UI_AVG_IND_2X 0x00 +#define BIT_GYR_UI_AVG_IND_4X 0x10 +#define BIT_GYR_UI_AVG_IND_8X 0x20 +#define BIT_GYR_UI_AVG_IND_16X 0x30 +#define BIT_GYR_UI_AVG_IND_32X 0x40 +#define BIT_GYR_UI_AVG_IND_64X 0x50 + +/* Bank0 REG_ACCEL_CONFIG1 */ +#define BIT_ACC_FILT_BW_IND_BYPASS 0x00 +#define BIT_ACC_FILT_BW_IND_180HZ 0x01 +#define BIT_ACC_FILT_BW_IND_121HZ 0x02 +#define BIT_ACC_FILT_BW_IND_73HZ 0x03 +#define BIT_ACC_FILT_BW_IND_53HZ 0x04 +#define BIT_ACC_FILT_BW_IND_34HZ 0x05 +#define BIT_ACC_FILT_BW_IND_25HZ 0x06 +#define BIT_ACC_FILT_BW_IND_16HZ 0x07 +#define BIT_ACC_UI_AVG_IND_2X 0x00 +#define BIT_ACC_UI_AVG_IND_4X 0x10 +#define BIT_ACC_UI_AVG_IND_8X 0x20 +#define BIT_ACC_UI_AVG_IND_16X 0x30 +#define BIT_ACC_UI_AVG_IND_32X 0x40 +#define BIT_ACC_UI_AVG_IND_64X 0x50 + +/* Bank0 REG_INT_CONFIG_REG */ +#define SHIFT_INT1_MODE 0x02 +#define SHIFT_INT1_DRIVE_CIRCUIT 0x01 +#define SHIFT_INT1_POLARITY 0x00 + +/* Bank0 REG_PWR_MGMT_0 */ +#define BIT_ACCEL_MODE_OFF 0x00 +#define BIT_ACCEL_MODE_LPM 0x02 +#define BIT_ACCEL_MODE_LNM 0x03 +#define BIT_ACCEL_MODE_MASK 0x03 +#define BIT_GYRO_MODE_OFF 0x00 +#define BIT_GYRO_MODE_STBY 0x04 +#define BIT_GYRO_MODE_LPM 0x08 +#define BIT_GYRO_MODE_LNM 0x0c +#define BIT_GYRO_MODE_MASK 0x0c +#define BIT_IDLE 0x10 +#define BIT_ACCEL_LP_CLK_SEL 0x80 + +/* Bank0 REG_SIGNAL_PATH_RESET */ +#define BIT_FIFO_FLUSH 0x04 +#define BIT_SOFT_RESET_CHIP_CONFIG 0x10 + +/* Bank0 REG_INTF_CONFIG0 */ +#define BIT_SIFS_CFG_I2C_ONLY 0x02 +#define BIT_SIFS_CFG_SPI_ONLY 0x03 +#define BIT_SENSOR_DATA_ENDIAN 0x10 +#define BIT_FIFO_COUNT_ENDIAN 0x20 +#define BIT_FIFO_COUNT_FORMAT 0x40 +#define BIT_FIFO_SREG_INVALID_IND_DIS 0x80 + +/* Bank0 REG_INTF_CONFIG1 */ +#define BIT_CLK_SEL_RC 0x00 +#define BIT_CLK_SEL_PLL 0x01 +#define BIT_CLK_SEL_DIS 0x03 +#define BIT_I3C_DDR_EN 0x04 +#define BIT_I3C_SDR_EN 0x08 +#define BIT_GYRO_AFSR_MODE_LFS 0x00 +#define BIT_GYRO_AFSR_MODE_HFS 0x20 +#define BIT_GYRO_AFSR_MODE_DYN 0x40 + +/* Bank0 REG_FIFO_CONFIG1 */ +#define BIT_FIFO_MODE_NO_BYPASS 0x00 +#define BIT_FIFO_MODE_BYPASS 0x01 +#define BIT_FIFO_MODE_STREAM 0x00 +#define BIT_FIFO_MODE_STOPFULL 0x02 + +/* Bank 0 REG_INT_SOURCE0 */ +#define BIT_INT_AGC_RDY_INT1_EN 0x01 +#define BIT_INT_FIFO_FULL_INT1_EN 0x02 +#define BIT_INT_FIFO_THS_INT1_EN 0x04 +#define BIT_INT_DRDY_INT_EN 0x08 +#define BIT_INT_RESET_DONE_INT1_EN 0x10 +#define BIT_INT_PLL_RDY_INT1_EN 0x20 +#define BIT_INT_FSYNC_INT1_EN 0x40 +#define BIT_INT_ST_DONE_INT1_EN 0x80 + +/* Bank 0 REG_INT_SOURCE1 */ +#define BIT_INT_WOM_X_INT1_EN 0x01 +#define BIT_INT_WOM_Y_INT1_EN 0x02 +#define BIT_INT_WOM_Z_INT1_EN 0x04 +#define BIT_INT_WOM_XYZ_INT1_EN (BIT_INT_WOM_X_INT1_EN | \ + BIT_INT_WOM_Y_INT1_EN | BIT_INT_WOM_Z_INT1_EN) +#define BIT_INT_SMD_INT1_EN 0x08 +#define BIT_INT_I3C_PROTCL_ERR_INT1_EN 0x40 + +/* Bank0 REG_INT_STATUS_DRDY */ +#define BIT_INT_STATUS_DRDY 0x01 + +/* Bank0 REG_INT_STATUS */ +#define BIT_INT_STATUS_AGC_RDY 0x01 +#define BIT_INT_STATUS_FIFO_FULL 0x02 +#define BIT_INT_STATUS_FIFO_THS 0x04 +#define BIT_INT_STATUS_RESET_DONE 0x10 +#define BIT_INT_STATUS_PLL_RDY 0x20 +#define BIT_INT_STATUS_FSYNC 0x40 +#define BIT_INT_STATUS_ST_DONE 0x80 + +/* Bank0 REG_INT_STATUS2 */ +#define BIT_INT_STATUS_WOM_Z 0x01 +#define BIT_INT_STATUS_WOM_Y 0x02 +#define BIT_INT_STATUS_WOM_X 0x04 +#define BIT_INT_STATUS_WOM_XYZ (BIT_INT_STATUS_WOM_X | \ + BIT_INT_STATUS_WOM_Y | BIT_INT_STATUS_WOM_Z) +#define BIT_INT_STATUS_SMD 0x08 + +/* Bank 0 REG_INT_STATUS3 */ +#define BIT_INT_STATUS_LOWG_DET 0x02 +#define BIT_INT_STATUS_FF_DET 0x04 +#define BIT_INT_STATUS_TILT_DET 0x08 +#define BIT_INT_STATUS_STEP_CNT_OVFL 0x10 +#define BIT_INT_STATUS_STEP_DET 0x20 + +/* Bank0 REG_WOM_CONFIG */ +#define BIT_WOM_EN_OFF 0x00 +#define BIT_WOM_EN_ON 0x01 +#define BIT_WOM_MODE_INITIAL 0x00 +#define BIT_WOM_MODE_PREV 0x02 +#define BIT_WOM_INT_MODE_OR 0x00 +#define BIT_WOM_INT_MODE_AND 0x04 +#define BIT_WOM_INT_DUR_LEGACY 0x00 +#define BIT_WOM_INT_DUR_2ND 0x08 +#define BIT_WOM_INT_DUR_3RD 0x10 +#define BIT_WOM_INT_DUR_4TH 0x18 + +/* Bank0 REG_APEX_CONFIG0 */ +#define BIT_DMP_SRAM_RESET_APEX 0x01 +#define BIT_DMP_INIT_EN 0x04 +#define BIT_DMP_POWER_SAVE_EN 0x08 + +/* Bank0 REG_APEX_CONFIG1 */ +#define BIT_DMP_ODR_25HZ 0x00 +#define BIT_DMP_ODR_50HZ 0x02 +#define BIT_DMP_ODR_100HZ 0x03 +#define BIT_DMP_PEDO_EN 0x08 +#define BIT_DMP_TILT_EN 0x10 +#define BIT_DMP_FF_EN 0x20 +#define BIT_DMP_SMD_EN 0x40 + +/* REG_OTP_CONFIG_MREG_TOP1 */ +#define BIT_OTP_COPY_NORMAL 0x04 +#define BIT_OTP_COPY_ST_DATA 0x0C +#define OTP_COPY_MODE_MASK 0x0C + +/* REG_INT_SOURCE6_MREG_TOP1 */ +#define BIT_INT_TLT_DET_INT1_EN 0x08 +#define BIT_INT_STEP_CNT_OVFL_INT1_EN 0x10 +#define BIT_INT_STEP_DET_INT1_EN 0x20 +#define BIT_INT_LOWG_INT1_EN 0x40 +#define BIT_INT_FF_INT1_EN 0x80 + +/* REG_TMST_CONFIG1_MREG_TOP1 */ +#define BIT_TMST_EN 0x01 +#define BIT_TMST_FSYNC_EN 0x02 +#define BIT_TMST_DELTA_EN 0x04 +#define BIT_TMST_RESOL 0x08 +#define BIT_TMST_ON_SREG_EN 0x10 +#define BIT_ODR_EN_WITHOUT_SENSOR 0x40 + +/* REG_FIFO_CONFIG5_MREG_TOP1 */ +#define BIT_FIFO_ACCEL_EN 0x01 +#define BIT_FIFO_GYRO_EN 0x02 +#define BIT_FIFO_TMST_FSYNC_EN 0x04 +#define BIT_FIFO_HIRES_EN 0x08 +#define BIT_RESUME_PARTIAL_RD 0x10 +#define BIT_WM_GT_TH 0x20 + +/* REG_SELFTEST_MREG_TOP1 */ +#define BIT_EN_AX_ST 0x01 +#define BIT_EN_AY_ST 0x02 +#define BIT_EN_AZ_ST 0x04 +#define BIT_EN_GX_ST 0x08 +#define BIT_EN_GY_ST 0x10 +#define BIT_EN_GZ_ST 0x20 +#define BIT_ACCEL_ST_EN 0x40 +#define BIT_GYRO_ST_EN 0x80 + +/* REG_ST_CONFIG_MREG_TOP1 */ +#define BIT_PD_ACCEL_CP45_ST_REG 0x80 +#define SHIFT_GYRO_ST_LIM 0 +#define SHIFT_ACCEL_ST_LIM 3 +#define SHIFT_ST_NUM_SAMPLE 6 + +/* REG_ST_STATUS1_MREG_TOP1 */ +#define BIT_DMP_AX_ST_PASS 0x02 +#define BIT_DMP_AY_ST_PASS 0x04 +#define BIT_DMP_AZ_ST_PASS 0x08 +#define BIT_DMP_ACCEL_ST_DONE 0x10 +#define BIT_DMP_ACCEL_ST_PASS 0x20 + +/* REG_ST_STATUS2_MREG_TOP1 */ +#define BIT_DMP_GX_ST_PASS 0x02 +#define BIT_DMP_GY_ST_PASS 0x04 +#define BIT_DMP_GZ_ST_PASS 0x08 +#define BIT_DMP_GYRO_ST_DONE 0x10 +#define BIT_DMP_GYRO_ST_PASS 0x20 +#define BIT_DMP_ST_INCOMPLETE 0x40 + +/* REG_OTP_CTRL7_MREG_OTP */ +#define BIT_OTP_RELOAD 0x08 +#define BIT_OTP_PWR_DOWN 0x02 + + +/* fifo data packet header */ +#define BIT_FIFO_HEAD_MSG 0x80 +#define BIT_FIFO_HEAD_ACCEL 0x40 +#define BIT_FIFO_HEAD_GYRO 0x20 +#define BIT_FIFO_HEAD_20 0x10 +#define BIT_FIFO_HEAD_TMSP_ODR 0x08 +#define BIT_FIFO_HEAD_TMSP_NO_ODR 0x04 +#define BIT_FIFO_HEAD_TMSP_FSYNC 0x0C +#define BIT_FIFO_HEAD_ODR_ACCEL 0x02 +#define BIT_FIFO_HEAD_ODR_GYRO 0x01 + +/* data definitions */ +#define FIFO_PACKET_BYTE_SINGLE 8 +#define FIFO_PACKET_BYTE_6X 16 +#define FIFO_PACKET_BYTE_HIRES 20 +#define FIFO_COUNT_BYTE 2 + +/* sensor startup time */ +#define INV_ICM43600_GYRO_START_TIME 100 +#define INV_ICM43600_ACCEL_START_TIME 100 + +/* sensor stop time */ +#define INV_ICM43600_GYRO_STOP_TIME 20 + +/* M-reg access wait tile */ +#define INV_ICM42607_MCLK_WAIT_US 20 +#define INV_ICM42607_BLK_SEL_WAIT_US 10 +#define INV_ICM42607_MADDR_WAIT_US 10 +#define INV_ICM42607_M_RW_WAIT_US 10 + +/* temperature sensor */ +#define TEMP_SCALE 100 /* scale by 100 */ +#define TEMP_LSB_PER_DEG 2 /* 2LSB=1degC */ +#define TEMP_OFFSET 25 /* 25 degC */ + +/* + * INT configurations + * Polarity: 0 -> Active Low, 1 -> Active High + * Drive circuit: 0 -> Open Drain, 1 -> Push-Pull + * Mode: 0 -> Pulse, 1 -> Latch + */ +#define INT_POLARITY 1 +#define INT_DRIVE_CIRCUIT 1 +#define INT_MODE 0 + +#define ICM42607_DEVICE_ID 0x61 +#define ICM4260X_PRECISION 16 +#define ICM42607_ADDR 0x68 + +#endif diff --git a/include/linux/sensor-dev.h b/include/linux/sensor-dev.h index 81d3aa1ea38b..a29d512ec0d1 100644 --- a/include/linux/sensor-dev.h +++ b/include/linux/sensor-dev.h @@ -70,6 +70,7 @@ enum sensor_id { ACCEL_ID_DA215S, ACCEL_ID_DA228E, ACCEL_ID_IAM20680, + ACCEL_ID_ICM4260X, COMPASS_ID_ALL, COMPASS_ID_AK8975, COMPASS_ID_AK8963, @@ -97,6 +98,7 @@ enum sensor_id { GYRO_ID_LSM330, GYRO_ID_ICM2060X, GYRO_ID_IAM20680, + GYRO_ID_ICM4260X, LIGHT_ID_ALL, LIGHT_ID_CM3217, LIGHT_ID_CM3218, diff --git a/include/soc/rockchip/rockchip_amp.h b/include/soc/rockchip/rockchip_amp.h index 1c37e8b46e0b..851c0f47d54a 100644 --- a/include/soc/rockchip/rockchip_amp.h +++ b/include/soc/rockchip/rockchip_amp.h @@ -9,15 +9,18 @@ #ifndef _ROCKCHIP_AMP #define _ROCKCHIP_AMP +#include + #if IS_REACHABLE(CONFIG_ROCKCHIP_AMP) -void rockchip_amp_get_gic_info(void); +void rockchip_amp_get_gic_info(u32 spis_num, enum gic_type gic_version); int rockchip_amp_check_amp_irq(u32 irq); u32 rockchip_amp_get_irq_prio(u32 irq); u32 rockchip_amp_get_irq_cpumask(u32 irq); +u64 rockchip_amp_get_irq_aff(u32 irq); +int rockchip_amp_need_init_amp_irq(u32 irq); #else -#include - -static inline void rockchip_amp_get_gic_info(void) +static inline void rockchip_amp_get_gic_info(u32 spis_num, + enum gic_type gic_version) { } @@ -35,5 +38,11 @@ static inline u32 rockchip_amp_get_irq_cpumask(u32 irq) { return 0; } + +static inline int rockchip_amp_need_init_amp_irq(u32 irq) +{ + return 0; +} + #endif /* CONFIG_ROCKCHIP_AMP */ #endif /* _ROCKCHIP_AMP */ diff --git a/include/uapi/linux/rk_hdmirx_config.h b/include/uapi/linux/rk_hdmirx_config.h index a0b7e436aed9..26dcea790013 100644 --- a/include/uapi/linux/rk_hdmirx_config.h +++ b/include/uapi/linux/rk_hdmirx_config.h @@ -82,4 +82,7 @@ enum hdmirx_color_space { #define RK_HDMIRX_V4L2_EVENT_SIGNAL_LOST \ (V4L2_EVENT_PRIVATE_START + 1) +#define RK_HDMIRX_V4L2_EVENT_AUDIOINFO \ + (V4L2_EVENT_PRIVATE_START + 2) + #endif /* _UAPI_RK_HDMIRX_CONFIG_H */ diff --git a/sound/soc/rockchip/rockchip_multi_dais_pcm.c b/sound/soc/rockchip/rockchip_multi_dais_pcm.c index d52bd1e91395..a3e9b59da49c 100644 --- a/sound/soc/rockchip/rockchip_multi_dais_pcm.c +++ b/sound/soc/rockchip/rockchip_multi_dais_pcm.c @@ -33,6 +33,7 @@ struct dmaengine_mpcm { struct dmaengine_mpcm_runtime_data { struct dma_chan *chans[MAX_DAIS]; + struct dma_interleaved_template *xt; dma_cookie_t cookies[MAX_DAIS]; unsigned int *channel_maps; int num_chans; @@ -133,33 +134,71 @@ static void dmaengine_mpcm_get_master_chan(struct dmaengine_mpcm_runtime_data *p } } +static int dmaengine_config_interleaved(struct snd_pcm_substream *substream, + struct dma_interleaved_template *xt, + int offset, int sample_bytes, int nump, int numf) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int frame_bytes; + + frame_bytes = frames_to_bytes(runtime, 1); + + xt->frame_size = 1; + xt->sgl[0].size = sample_bytes; + xt->sgl[0].icg = frame_bytes - sample_bytes; + +#ifdef CONFIG_NO_GKI + xt->nump = nump; +#endif + xt->numf = numf; + + xt->dir = snd_pcm_substream_to_dma_direction(substream); + + if (xt->dir == DMA_MEM_TO_DEV) { + xt->src_start = runtime->dma_addr + offset; + xt->src_inc = true; + xt->src_sgl = true; + xt->dst_inc = false; + xt->dst_sgl = false; + } else { + xt->dst_start = runtime->dma_addr + offset; + xt->src_inc = false; + xt->src_sgl = false; + xt->dst_inc = true; + xt->dst_sgl = true; + } + + return 0; +} + static int dmaengine_mpcm_prepare_and_submit(struct snd_pcm_substream *substream) { struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct dma_async_tx_descriptor *desc = NULL; - enum dma_transfer_direction direction; + struct dma_interleaved_template *xt = prtd->xt; unsigned long flags = DMA_CTRL_ACK; unsigned int *maps = prtd->channel_maps; - int offset, buffer_bytes, period_bytes; + int offset; int i; - direction = snd_pcm_substream_to_dma_direction(substream); - if (!substream->runtime->no_period_wakeup) flags |= DMA_PREP_INTERRUPT; prtd->pos = 0; offset = 0; - period_bytes = snd_pcm_lib_period_bytes(substream); - buffer_bytes = snd_pcm_lib_buffer_bytes(substream); + for (i = 0; i < prtd->num_chans; i++) { if (!prtd->chans[i]) continue; - desc = dmaengine_prep_dma_cyclic(prtd->chans[i], - runtime->dma_addr + offset, - buffer_bytes, period_bytes, - direction, flags); + + dmaengine_config_interleaved(substream, xt, offset, + samples_to_bytes(runtime, maps[i]), + runtime->period_size, + runtime->buffer_size); + + desc = dmaengine_prep_interleaved_dma(prtd->chans[i], xt, + flags | DMA_PREP_REPEAT); if (!desc) return -ENOMEM; @@ -247,33 +286,34 @@ static void dmaengine_mpcm_single_dma_complete(void *arg) } static int __mpcm_prepare_single_and_submit(struct snd_pcm_substream *substream, - dma_addr_t buf_start, int size) + int buf_offset, int size) { struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_interleaved_template *xt = prtd->xt; struct snd_pcm_runtime *runtime = substream->runtime; struct dma_async_tx_descriptor *desc; - enum dma_transfer_direction direction; unsigned long flags = DMA_CTRL_ACK; unsigned int *maps = prtd->channel_maps; int offset, i; bool callback = false; - direction = snd_pcm_substream_to_dma_direction(substream); - if (!substream->runtime->no_period_wakeup) flags |= DMA_PREP_INTERRUPT; - offset = 0; + offset = buf_offset; for (i = 0; i < prtd->num_chans; i++) { if (!prtd->chans[i]) continue; - desc = dmaengine_prep_slave_single(prtd->chans[i], - buf_start + offset, - size, - direction, flags); + dmaengine_config_interleaved(substream, xt, offset, + samples_to_bytes(runtime, maps[i]), + 0, + bytes_to_frames(runtime, size)); + + desc = dmaengine_prep_interleaved_dma(prtd->chans[i], xt, flags); if (!desc) return -ENOMEM; + if (!callback) { desc->callback = dmaengine_mpcm_single_dma_complete; desc->callback_param = substream; @@ -314,15 +354,15 @@ static int dmaengine_mpcm_prepare_single_and_submit(struct snd_pcm_substream *su pr_debug("%s: offset: %d, buffer_bytes: %d\n", __func__, offset, buffer_bytes); pr_debug("%s: count: %d, residue_bytes: %d\n", __func__, count, residue_bytes); for (i = 0; i < count; i++) { - ret = __mpcm_prepare_single_and_submit(substream, buf_start, + ret = __mpcm_prepare_single_and_submit(substream, offset, period_bytes); if (ret) return ret; - buf_start += period_bytes; + offset += period_bytes; } if (residue_bytes) { - ret = __mpcm_prepare_single_and_submit(substream, buf_start, + ret = __mpcm_prepare_single_and_submit(substream, offset, residue_bytes); if (ret) return ret; @@ -421,22 +461,12 @@ static int dmaengine_mpcm_hw_params(struct snd_soc_component *component, sz = snd_pcm_format_size(format, maps[i]); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { chan = pcm->tx_chans[i]; -#ifdef CONFIG_NO_GKI - if (sz) { - slave_config.src_interlace_size = frame_bytes - sz; - if (slave_config.src_interlace_size) - slave_config.dst_maxburst = sz / slave_config.dst_addr_width; - } -#endif + if (sz && (frame_bytes - sz) > 0) + slave_config.dst_maxburst = sz / slave_config.dst_addr_width; } else { chan = pcm->rx_chans[i]; -#ifdef CONFIG_NO_GKI - if (sz) { - slave_config.dst_interlace_size = frame_bytes - sz; - if (slave_config.dst_interlace_size) - slave_config.src_maxburst = sz / slave_config.src_addr_width; - } -#endif + if (sz && (frame_bytes - sz) > 0) + slave_config.src_maxburst = sz / slave_config.src_addr_width; } if (!chan) continue; @@ -540,6 +570,13 @@ static int dmaengine_mpcm_open(struct snd_soc_component *component, if (!prtd) return -ENOMEM; + prtd->xt = kzalloc(sizeof(struct dma_interleaved_template) + + sizeof(struct data_chunk), GFP_KERNEL); + if (!prtd->xt) { + kfree(prtd); + return -ENOMEM; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { prtd->channel_maps = pcm->mdais->playback_channel_maps; for (i = 0; i < pcm->mdais->num_dais; i++) @@ -640,6 +677,7 @@ static int dmaengine_mpcm_close(struct snd_soc_component *component, { struct dmaengine_mpcm_runtime_data *prtd = substream_to_prtd(substream); + kfree(prtd->xt); kfree(prtd); return 0;