diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile index b113b5b6aa35..21a898fea122 100644 --- a/arch/arm64/boot/dts/rockchip/Makefile +++ b/arch/arm64/boot/dts/rockchip/Makefile @@ -159,6 +159,17 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3567-evb2-lp4x-v10-dual-channel-lvds.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3567-evb2-lp4x-v10-one-vp-two-single-channel-lvds.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3567-evb2-lp4x-v10-single-channel-lvds.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3567-evb2-lp4x-v10-two-vp-two-separate-single-channel-lvds.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-hdmi2bt1120-ddr4-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-hdmi2dsi-ddr4-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-hdmi2dsi-dual-ddr4-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-hdmi2gvi-ddr4-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-hdmi2lvds-ddr4-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-hdmi2lvds-dual-ddr4-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-rgb2dsi-ddr4-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-rgb2gvi-ddr4-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-rgb2hdmi-ddr4-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-rgb2lvds-ddr4-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb-rk628-rgb2lvds-dual-ddr4-v10.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb1-ddr4-v10.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb1-ddr4-v10-dual-camera.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb1-ddr4-v10-one-vp-two-single-channel-lvds.dtb @@ -257,6 +268,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb7-lp4-v10-rk1608-ipc-8x-linux.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb7-lp4-v11-linux-ipc.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb7-v11.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb7-v11-linux.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb7-v11-rk628-hdmi2csi.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-nvr-demo-v10.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-nvr-demo-v10-android.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-nvr-demo-v10-ipc-4x-linux.dtb diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-ddr4-v10.dtsi b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-ddr4-v10.dtsi new file mode 100644 index 000000000000..eb859d9aeaba --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-ddr4-v10.dtsi @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +/dts-v1/; + +#include +#include +#include "rk3568.dtsi" +#include "rk3568-evb.dtsi" + +/ { + model = "Rockchip RK3568 EVB RK628 DDR4 V10 Board"; + compatible = "rockchip,rk3568-evb-rk628-ddr4-v10", "rockchip,rk3568"; + + rk628_sound: rk628-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <128>; + simple-audio-card,name = "rockchip,hdmi-rk628"; + status = "disabled"; + + simple-audio-card,cpu { + sound-dai = <&i2s2_2ch>; + }; + simple-audio-card,codec { + sound-dai = <&i2c2_rk628>; + }; + }; +}; + +&i2c2 { + clock-frequency = <400000>; + status = "okay"; + + i2c2_rk628: rk628@50 { + compatible = "rockchip,rk628"; + reg = <0x50>; + interrupt-parent = <&gpio0>; + interrupts = <20 IRQ_TYPE_LEVEL_HIGH>; + enable-gpios = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio2 RK_PA2 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&rk628_reset>; + status = "okay"; + }; +}; + +&i2c3 { + clock-frequency = <400000>; + status = "okay"; + + i2c3_rk628: rk628@51 { + compatible = "rockchip,rk628"; + reg = <0x51>; + + /* external test board + * rk628_int1: gpio1_a5 + * rk628_reset1: gpio1_a6 + * rk628_int2: gpio1_a7 + * rk628_reset2: gpio1_b0 + */ + interrupt-parent = <&gpio1>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + reset-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_LOW>; + status = "disabled"; + }; +}; + +&i2c4 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c4m1_xfer>; + clock-frequency = <400000>; + status = "okay"; + + i2c4_rk628: rk628@50 { + compatible = "rockchip,rk628"; + reg = <0x50>; + + interrupt-parent = <&gpio2>; + interrupts = <15 IRQ_TYPE_LEVEL_HIGH>; + enable-gpios = <&gpio2 RK_PC0 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_LOW>; + status = "disabled"; + }; +}; + +&i2c5 { + status = "disabled"; +}; + +&i2s1_8ch { + status = "disabled"; +}; + +&pinctrl { + rk628 { + rk628_reset: rk628-reset { + rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&pwm5 { + status = "disabled"; +}; + +&pwm7 { + status = "disabled"; +}; + +&pmu_io_domains { + status = "okay"; + pmuio1-supply = <&vcc3v3_pmu>; + pmuio2-supply = <&vcc3v3_pmu>; + vccio1-supply = <&vcc_3v3>; + vccio3-supply = <&vcc_3v3>; + vccio4-supply = <&vcc_3v3>; + vccio5-supply = <&vcc_3v3>; + vccio6-supply = <&vcc_3v3>; + vccio7-supply = <&vcc_3v3>; +}; + +&sdmmc0 { + status = "disabled"; +}; + +&spdif_8ch { + status = "disabled"; +}; + +&spdif_out { + status = "disabled"; +}; + +&u2phy0_otg { + /delete-property/ vbus-supply; + status = "okay"; +}; + +&usbdrd_dwc3 { + dr_mode = "otg"; + phys = <&u2phy0_otg>; + phy-names = "usb2-phy"; + extcon = <&usb2phy0>; + maximum-speed = "high-speed"; + snps,dis_u2_susphy_quirk; + status = "okay"; +}; + +&vcc3v3_lcd0_n { + gpio = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>; + enable-active-high; +}; + +&vcc3v3_lcd1_n { + status = "disabled"; +}; + +&wireless_wlan { + status = "disabled"; +}; + +&wireless_bluetooth { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2bt1120-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2bt1120-ddr4-v10.dts new file mode 100644 index 000000000000..13302a071a7a --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2bt1120-ddr4-v10.dts @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2023 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" + +&i2c4 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c4m1_xfer>; + clock-frequency = <400000>; + status = "okay"; + + rk628_bt1120: rk628_bt1120@50 { + compatible = "rockchip,rk628-bt1120-v4l2"; + reg = <0x50>; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&cif_dvp_clk &cif_dvp_bus16 &cif_dvp_bus8>; + interrupt-parent = <&gpio2>; + interrupts = <15 IRQ_TYPE_LEVEL_HIGH>; + enable-gpios = <&gpio2 RK_PC0 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_LOW>; + plugin-det-gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_LOW>; + rockchip,camera-module-index = <0>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "RK628-BT1120"; + rockchip,camera-module-lens-name = "NC"; + dual-edge = <1>; + + port { + lt8619c_out: endpoint { + remote-endpoint = <&cif_para_in>; + bus-width = <16>; + pclk-sample = <1>; + }; + }; + }; +}; + +&rkcif_dvp { + status = "okay"; + + port { + /* Parallel bus endpoint */ + cif_para_in: endpoint { + remote-endpoint = <<8619c_out>; + }; + }; +}; + +&rkcif { + status = "okay"; +}; + +&rkcif_mmu { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2dsi-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2dsi-ddr4-v10.dts new file mode 100644 index 000000000000..4ecd56149a24 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2dsi-ddr4-v10.dts @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" +#include + +>1x { + status = "okay"; + power-supply = <&vcc3v3_lcd0_n>; + goodix,rst-gpio = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>; + goodix,irq-gpio = <&gpio0 RK_PA4 IRQ_TYPE_LEVEL_LOW>; +}; + +&i2c2_rk628 { + panel-backlight = <&backlight>; + panel-power-supply = <&vcc3v3_lcd0_n>; + panel-reset-delay-ms = <10>; + panel-enable-delay-ms = <10>; + panel-prepare-delay-ms = <60>; + panel-unprepare-delay-ms = <10>; + panel-disable-delay-ms = <60>; + + rk628,hdmi-in; + rk628-dsi { + //rockchip,dual-channel; + dsi,eotp; + dsi,video-mode; + dsi,format = "rgb888"; + dsi,lanes = <4>; + status = "okay"; + + rk628-panel { + panel-init-sequence = [ + 23 00 02 FE 21 + 23 00 02 04 00 + 23 00 02 00 64 + 23 00 02 2A 00 + 23 00 02 26 64 + 23 00 02 54 00 + 23 00 02 50 64 + 23 00 02 7B 00 + 23 00 02 77 64 + 23 00 02 A2 00 + 23 00 02 9D 64 + 23 00 02 C9 00 + 23 00 02 C5 64 + 23 00 02 01 71 + 23 00 02 27 71 + 23 00 02 51 71 + 23 00 02 78 71 + 23 00 02 9E 71 + 23 00 02 C6 71 + 23 00 02 02 89 + 23 00 02 28 89 + 23 00 02 52 89 + 23 00 02 79 89 + 23 00 02 9F 89 + 23 00 02 C7 89 + 23 00 02 03 9E + 23 00 02 29 9E + 23 00 02 53 9E + 23 00 02 7A 9E + 23 00 02 A0 9E + 23 00 02 C8 9E + 23 00 02 09 00 + 23 00 02 05 B0 + 23 00 02 31 00 + 23 00 02 2B B0 + 23 00 02 5A 00 + 23 00 02 55 B0 + 23 00 02 80 00 + 23 00 02 7C B0 + 23 00 02 A7 00 + 23 00 02 A3 B0 + 23 00 02 CE 00 + 23 00 02 CA B0 + 23 00 02 06 C0 + 23 00 02 2D C0 + 23 00 02 56 C0 + 23 00 02 7D C0 + 23 00 02 A4 C0 + 23 00 02 CB C0 + 23 00 02 07 CF + 23 00 02 2F CF + 23 00 02 58 CF + 23 00 02 7E CF + 23 00 02 A5 CF + 23 00 02 CC CF + 23 00 02 08 DD + 23 00 02 30 DD + 23 00 02 59 DD + 23 00 02 7F DD + 23 00 02 A6 DD + 23 00 02 CD DD + 23 00 02 0E 15 + 23 00 02 0A E9 + 23 00 02 36 15 + 23 00 02 32 E9 + 23 00 02 5F 15 + 23 00 02 5B E9 + 23 00 02 85 15 + 23 00 02 81 E9 + 23 00 02 AD 15 + 23 00 02 A9 E9 + 23 00 02 D3 15 + 23 00 02 CF E9 + 23 00 02 0B 14 + 23 00 02 33 14 + 23 00 02 5C 14 + 23 00 02 82 14 + 23 00 02 AA 14 + 23 00 02 D0 14 + 23 00 02 0C 36 + 23 00 02 34 36 + 23 00 02 5D 36 + 23 00 02 83 36 + 23 00 02 AB 36 + 23 00 02 D1 36 + 23 00 02 0D 6B + 23 00 02 35 6B + 23 00 02 5E 6B + 23 00 02 84 6B + 23 00 02 AC 6B + 23 00 02 D2 6B + 23 00 02 13 5A + 23 00 02 0F 94 + 23 00 02 3B 5A + 23 00 02 37 94 + 23 00 02 64 5A + 23 00 02 60 94 + 23 00 02 8A 5A + 23 00 02 86 94 + 23 00 02 B2 5A + 23 00 02 AE 94 + 23 00 02 D8 5A + 23 00 02 D4 94 + 23 00 02 10 D1 + 23 00 02 38 D1 + 23 00 02 61 D1 + 23 00 02 87 D1 + 23 00 02 AF D1 + 23 00 02 D5 D1 + 23 00 02 11 04 + 23 00 02 39 04 + 23 00 02 62 04 + 23 00 02 88 04 + 23 00 02 B0 04 + 23 00 02 D6 04 + 23 00 02 12 05 + 23 00 02 3A 05 + 23 00 02 63 05 + 23 00 02 89 05 + 23 00 02 B1 05 + 23 00 02 D7 05 + 23 00 02 18 AA + 23 00 02 14 36 + 23 00 02 42 AA + 23 00 02 3D 36 + 23 00 02 69 AA + 23 00 02 65 36 + 23 00 02 8F AA + 23 00 02 8B 36 + 23 00 02 B7 AA + 23 00 02 B3 36 + 23 00 02 DD AA + 23 00 02 D9 36 + 23 00 02 15 74 + 23 00 02 3F 74 + 23 00 02 66 74 + 23 00 02 8C 74 + 23 00 02 B4 74 + 23 00 02 DA 74 + 23 00 02 16 9F + 23 00 02 40 9F + 23 00 02 67 9F + 23 00 02 8D 9F + 23 00 02 B5 9F + 23 00 02 DB 9F + 23 00 02 17 DC + 23 00 02 41 DC + 23 00 02 68 DC + 23 00 02 8E DC + 23 00 02 B6 DC + 23 00 02 DC DC + 23 00 02 1D FF + 23 00 02 19 03 + 23 00 02 47 FF + 23 00 02 43 03 + 23 00 02 6E FF + 23 00 02 6A 03 + 23 00 02 94 FF + 23 00 02 90 03 + 23 00 02 BC FF + 23 00 02 B8 03 + 23 00 02 E2 FF + 23 00 02 DE 03 + 23 00 02 1A 35 + 23 00 02 44 35 + 23 00 02 6B 35 + 23 00 02 91 35 + 23 00 02 B9 35 + 23 00 02 DF 35 + 23 00 02 1B 45 + 23 00 02 45 45 + 23 00 02 6C 45 + 23 00 02 92 45 + 23 00 02 BA 45 + 23 00 02 E0 45 + 23 00 02 1C 55 + 23 00 02 46 55 + 23 00 02 6D 55 + 23 00 02 93 55 + 23 00 02 BB 55 + 23 00 02 E1 55 + 23 00 02 22 FF + 23 00 02 1E 68 + 23 00 02 4C FF + 23 00 02 48 68 + 23 00 02 73 FF + 23 00 02 6F 68 + 23 00 02 99 FF + 23 00 02 95 68 + 23 00 02 C1 FF + 23 00 02 BD 68 + 23 00 02 E7 FF + 23 00 02 E3 68 + 23 00 02 1F 7E + 23 00 02 49 7E + 23 00 02 70 7E + 23 00 02 96 7E + 23 00 02 BE 7E + 23 00 02 E4 7E + 23 00 02 20 97 + 23 00 02 4A 97 + 23 00 02 71 97 + 23 00 02 97 97 + 23 00 02 BF 97 + 23 00 02 E5 97 + 23 00 02 21 B5 + 23 00 02 4B B5 + 23 00 02 72 B5 + 23 00 02 98 B5 + 23 00 02 C0 B5 + 23 00 02 E6 B5 + 23 00 02 25 F0 + 23 00 02 23 E8 + 23 00 02 4F F0 + 23 00 02 4D E8 + 23 00 02 76 F0 + 23 00 02 74 E8 + 23 00 02 9C F0 + 23 00 02 9A E8 + 23 00 02 C4 F0 + 23 00 02 C2 E8 + 23 00 02 EA F0 + 23 00 02 E8 E8 + 23 00 02 24 FF + 23 00 02 4E FF + 23 00 02 75 FF + 23 00 02 9B FF + 23 00 02 C3 FF + 23 00 02 E9 FF + 23 00 02 FE 3D + 23 00 02 00 04 + 23 00 02 FE 23 + 23 00 02 08 82 + 23 00 02 0A 00 + 23 00 02 0B 00 + 23 00 02 0C 01 + 23 00 02 16 00 + 23 00 02 18 02 + 23 00 02 1B 04 + 23 00 02 19 04 + 23 00 02 1C 81 + 23 00 02 1F 00 + 23 00 02 20 03 + 23 00 02 23 04 + 23 00 02 21 01 + 23 00 02 54 63 + 23 00 02 55 54 + 23 00 02 6E 45 + 23 00 02 6D 36 + 23 00 02 FE 3D + 23 00 02 55 78 + 23 00 02 FE 20 + 23 00 02 26 30 + 23 00 02 FE 3D + 23 00 02 20 71 + 23 00 02 50 8F + 23 00 02 51 8F + 23 00 02 FE 00 + 23 00 02 35 00 + 05 78 01 11 + 05 00 01 29 + ]; + + panel-exit-sequence = [ + 05 00 01 28 + 05 00 01 10 + ]; + }; + }; + + display-timings { + src-timing { + clock-frequency = <132000000>; + hactive = <1080>; + vactive = <1920>; + hback-porch = <30>; + hfront-porch = <60>; + vback-porch = <10>; + vfront-porch = <10>; + hsync-len = <40>; + vsync-len = <10>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; + + dst-timing { + clock-frequency = <132000000>; + hactive = <1080>; + vactive = <1920>; + hback-porch = <30>; + hfront-porch = <60>; + vback-porch = <10>; + vfront-porch = <10>; + hsync-len = <40>; + vsync-len = <10>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; +}; + +&hdmi { + status = "okay"; + force-bus-format = ; + force-output; + force_timing{ + clock-frequency = <132000000>; + hactive = <1080>; + vactive = <1920>; + hback-porch = <30>; + hfront-porch = <60>; + vback-porch = <10>; + vfront-porch = <10>; + hsync-len = <40>; + vsync-len = <10>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; +}; + +&hdmi_in_vp0 { + status = "okay"; +}; + +&hdmi_in_vp1 { + status = "disabled"; +}; + +&route_hdmi { + status = "okay"; + + force-bus-format = ; + force-output; + force_timing{ + clock-frequency = <132000000>; + hactive = <1080>; + vactive = <1920>; + hback-porch = <30>; + hfront-porch = <60>; + vback-porch = <10>; + vfront-porch = <10>; + hsync-len = <40>; + vsync-len = <10>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; +}; + +&touch_gpio { + rockchip,pins = <0 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>, + <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2dsi-dual-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2dsi-dual-ddr4-v10.dts new file mode 100644 index 000000000000..0892e5e9ee47 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2dsi-dual-ddr4-v10.dts @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" +#include + +>1x { + status = "disabled"; +}; + +&i2c2_rk628 { + panel-backlight = <&backlight>; + panel-power-supply = <&vcc3v3_lcd0_n>; + panel-reset-delay-ms = <240>; + panel-enable-delay-ms = <240>; + panel-prepare-delay-ms = <240>; + panel-unprepare-delay-ms = <240>; + panel-disable-delay-ms = <240>; + panel-init-delay-ms = <240>; + + rk628,hdmi-in; + rk628-dsi { + rockchip,lane-mbps = <1100>; + rockchip,dual-channel; + dsi,eotp; + dsi,video-mode; + dsi,format = "rgb888"; + dsi,lanes = <4>; + status = "okay"; + + rk628-panel { + panel-init-sequence = [ + 29 00 05 ff aa 55 a5 80 + 23 00 02 6f 31 + 23 00 02 fa 00 + 23 00 02 6f 13 + 23 00 02 f4 00 + 23 00 02 fa 00 + 29 00 05 ff aa 55 a5 00 + 29 00 06 f0 55 aa 52 08 00 + 23 00 02 b1 21 + 29 00 03 b2 10 82 + 29 00 03 b2 87 22 + 29 00 04 b4 25 02 8c + 29 00 03 b5 0a 00 + 29 00 04 b8 00 04 02 + 29 00 0a b9 03 00 18 11 31 44 03 51 00 + 29 00 03 ba 00 32 + 29 00 03 bc 4f 00 + 29 00 05 bd 00 b6 10 10 + 29 00 03 c6 11 10 + 29 00 06 f0 55 aa 52 08 01 + 29 00 03 b1 11 11 + 29 00 07 b2 08 08 08 08 08 08 + 29 00 07 b3 0f 19 0f 19 0f 19 + 29 00 07 b6 15 14 15 14 15 14 + 29 00 03 bc 5f 00 + 29 00 03 bd 5f 00 + 29 00 03 be 00 db + 23 00 02 ca 0f + 29 00 06 f0 55 aa 52 08 02 + 29 00 0f b0 00 0e 29 36 3f 48 51 3b 37 + 3c 3c 51 61 60 + 29 00 0f b1 59 6a 81 81 94 9b a8 a8 af + b6 bd c4 cc ff + 29 00 0f b2 00 0e 29 36 3f 48 51 3b 37 + 3c 3c 51 61 60 + 29 00 10 b3 59 6a 81 81 94 9b a8 a8 af + b6 bd c4 ca cc ff + 29 00 0f b4 00 0e 29 36 3f 48 51 3b 37 + 3c 3c 51 61 60 + 29 00 10 b5 59 6a 81 81 94 9b a8 a8 af + b6 bd c4 ca cc ff + 29 00 0f b6 00 0e 29 36 3f 48 51 3b 37 + 3c 3c 51 61 60 + 29 00 10 b7 59 6a 81 81 94 9b a8 a8 af + b6 bd c4 ca cc ff + 29 00 0f b8 00 0e 29 36 3f 48 51 3b 37 + 3c 3c 51 61 60 + 29 00 10 b9 59 6a 81 81 94 9b a8 a8 af + b6 bd c4 ca cc ff + 29 00 0f ba 00 0e 29 36 3f 48 51 3b 37 + 3c 3c 51 61 60 + 29 00 10 bb 59 6a 81 81 94 9b a8 a8 af + b6 bd c4 ca cc ff + 29 00 06 f0 55 aa 52 08 03 + 29 00 05 b0 00 00 00 00 + 29 00 05 b1 03 00 00 03 + 29 00 08 b2 00 00 64 00 64 05 02 + 29 00 03 b4 00 08 + 29 00 05 b5 12 28 06 06 + 29 00 06 ba 31 00 01 00 09 + 29 00 06 bb 31 00 01 00 09 + 29 00 06 f0 55 aa 52 08 05 + 29 00 04 b0 03 11 3f + 29 00 03 b2 06 60 + 29 00 03 b3 10 33 + 23 00 02 b4 26 + 29 00 04 b5 06 20 00 + 29 00 04 b6 86 e0 00 + 29 00 06 ba 8e 00 00 a4 00 + 29 00 06 bb 06 00 00 20 00 + 29 00 06 bc 8e 00 00 a4 00 + 29 00 06 bd 2e 00 00 a4 00 + 29 00 06 be 8e 00 00 a0 00 + 29 00 06 bf 06 00 00 24 00 + 23 00 02 c1 00 + 29 00 03 c8 05 10 + 29 00 03 c9 03 10 + 29 00 04 d0 00 0a 02 + 29 00 04 d1 00 0a 04 + 23 00 02 ec 12 + 23 00 02 ed 00 + 29 00 03 ee 03 00 + 29 00 06 f0 55 aa 52 08 06 + 29 00 06 b0 00 04 08 11 12 + 29 00 06 b1 1f 15 16 1f 1f + 29 00 06 b2 1f 13 1f 1f 1f + 29 00 06 b3 1f 19 19 19 19 + 29 00 06 b4 1b 1b 1b 1b 1d + 29 00 04 b5 1d 1d 1d + 29 00 06 b6 00 05 09 11 12 + 29 00 06 b7 1f 15 16 1f 1f + 29 00 06 b8 1f 13 1f 1f 1f + 29 00 06 b9 1f 19 19 19 19 + 29 00 06 ba 1b 1b 1b 1b 1d + 29 00 04 bb 1d 1d 1d + 29 00 06 c0 00 09 05 12 11 + 29 00 06 c1 1f 15 16 1f 1f + 29 00 06 c2 1f 13 1f 1f 1f + 29 00 06 c3 1f 19 19 19 19 + 29 00 06 c4 1b 1b 1b 1b 1d + 29 00 04 c5 1d 1d 1d + 29 00 06 c6 00 08 04 12 11 + 29 00 06 c7 1f 15 16 1f 1f + 29 00 06 c8 1f 13 1f 1f 1f + 29 00 06 c9 1f 19 19 19 19 + 29 00 06 ca 1b 1b 1b 1b 1d + 29 00 04 cb 1d 1d 1d + 29 78 04 d0 00 aa 0a + 05 78 01 11 + 05 78 01 29 + ]; + + panel-exit-sequence = [ + 05 00 01 28 + 05 00 01 10 + ]; + }; + }; + + display-timings { + src-timing { + clock-frequency = <264000000>; + hactive = <1440>; + vactive = <2560>; + hfront-porch = <150>; + hsync-len = <30>; + hback-porch = <60>; + vfront-porch = <20>; + vsync-len = <20>; + vback-porch = <20>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; + + dst-timing { + clock-frequency = <264000000>; + hactive = <1440>; + vactive = <2560>; + hfront-porch = <150>; + hsync-len = <30>; + hback-porch = <60>; + vfront-porch = <20>; + vsync-len = <20>; + vback-porch = <20>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; +}; + +&hdmi { + status = "okay"; + force-bus-format = ; + force-output; + force_timing{ + clock-frequency = <264000000>; + hactive = <1440>; + vactive = <2560>; + hfront-porch = <150>; + hsync-len = <30>; + hback-porch = <60>; + vfront-porch = <20>; + vsync-len = <20>; + vback-porch = <20>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; +}; + +&hdmi_in_vp0 { + status = "okay"; +}; + +&hdmi_in_vp1 { + status = "disabled"; +}; + +&route_hdmi { + status = "okay"; + force-bus-format = ; + force-output; + force_timing { + clock-frequency = <264000000>; + hactive = <1440>; + vactive = <2560>; + hfront-porch = <150>; + hsync-len = <30>; + hback-porch = <60>; + vfront-porch = <20>; + vsync-len = <20>; + vback-porch = <20>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2gvi-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2gvi-ddr4-v10.dts new file mode 100644 index 000000000000..93631f254526 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2gvi-ddr4-v10.dts @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +#include +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" + +&backlight { + enable-gpios = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>; +}; + +>1x { + status = "disabled"; +}; + +&i2c2_rk628 { + panel-backlight = <&backlight>; + panel-power-supply = <&vcc3v3_lcd0_n>; + panel-reset-delay-ms = <10>; + panel-enable-delay-ms = <150>; + panel-prepare-delay-ms = <60>; + panel-unprepare-delay-ms = <10>; + panel-disable-delay-ms = <60>; + pinctrl-names = "default"; + pinctrl-0 = <&rk628_reset &refclk_pins>; + assigned-clocks = <&pmucru CLK_WIFI>; + assigned-clock-rates = <24000000>; + clocks = <&pmucru CLK_WIFI>; + clock-names = "soc_24M"; + + rk628,hdmi-in; + rk628-gvi { + /* "rgb666" + * "rgb888" + * "rgb101010" + * "yuyv8" + * "yuyv10" + */ + bus-format = "rgb888"; + gvi,lanes = <8>; + //"rockchip,division-mode"; + //"rockchip, gvi-frm-rst"; + status = "okay"; + }; + + display-timings { + src-timing { + clock-frequency = <594000000>; + hactive = <3840>; + vactive = <2160>; + hback-porch = <296>; + hfront-porch = <176>; + vback-porch = <72>; + vfront-porch = <8>; + hsync-len = <88>; + vsync-len = <10>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; + + dst-timing { + clock-frequency = <594000000>; + hactive = <3840>; + vactive = <2160>; + hback-porch = <296>; + hfront-porch = <176>; + vback-porch = <72>; + vfront-porch = <8>; + hsync-len = <88>; + vsync-len = <10>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; +}; + +&hdmi { + status = "okay"; + force-bus-format = ; + force-output; + force_timing { + clock-frequency = <594000000>; + hactive = <3840>; + vactive = <2160>; + hback-porch = <296>; + hfront-porch = <176>; + vback-porch = <72>; + vfront-porch = <8>; + hsync-len = <88>; + vsync-len = <10>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; +}; + +&hdmi_in_vp0 { + status = "okay"; +}; + +&hdmi_in_vp1 { + status = "disabled"; +}; + +&route_hdmi { + status = "okay"; + force-bus-format = ; + force-output; + force_timing { + clock-frequency = <594000000>; + hactive = <3840>; + vactive = <2160>; + hback-porch = <296>; + hfront-porch = <176>; + vback-porch = <72>; + vfront-porch = <8>; + hsync-len = <88>; + vsync-len = <10>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; +}; + +&vcc3v3_lcd0_n { + gpio = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>; + enable-active-high; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2lvds-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2lvds-ddr4-v10.dts new file mode 100644 index 000000000000..66f0401fb329 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2lvds-ddr4-v10.dts @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" +#include + +>1x { + status = "disabled"; +}; + +&i2c2_rk628 { + panel-backlight = <&backlight>; + panel-power-supply = <&vcc3v3_lcd0_n>; + panel-enable-gpios = <&gpio2 RK_PC6 GPIO_ACTIVE_HIGH>; + panel-reset-delay-ms = <10>; + panel-enable-delay-ms = <10>; + panel-prepare-delay-ms = <60>; + panel-unprepare-delay-ms = <10>; + panel-disable-delay-ms = <60>; + + rk628,hdmi-in; + rk628-lvds { + /* "jeida_18","vesa_24","vesa_18" */ + bus-format = "jeida_24"; + + /* "dual_link_odd_even_pixels" + * "dual_link_even_odd_pixels" + * "dual_link_left_right_pixels" + * "dual_link_right_left_pixels" + */ + link-type = "single_link"; + status = "okay"; + }; + + display-timings { + src-timing { + clock-frequency = <66000000>; + hactive = <800>; + vactive = <1280>; + hback-porch = <30>; + hfront-porch = <30>; + vback-porch = <3>; + vfront-porch = <3>; + hsync-len = <10>; + vsync-len = <2>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; + + dst-timing { + clock-frequency = <66000000>; + hactive = <800>; + vactive = <1280>; + hback-porch = <30>; + hfront-porch = <30>; + vback-porch = <3>; + vfront-porch = <3>; + hsync-len = <10>; + vsync-len = <2>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; +}; + +&hdmi { + status = "okay"; + force-bus-format = ; + force-output; + force_timing{ + clock-frequency = <66000000>; + hactive = <800>; + vactive = <1280>; + hback-porch = <30>; + hfront-porch = <30>; + vback-porch = <3>; + vfront-porch = <3>; + hsync-len = <10>; + vsync-len = <2>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; +}; + +&hdmi_in_vp0 { + status = "okay"; +}; + +&hdmi_in_vp1 { + status = "disabled"; +}; + +&route_hdmi { + status = "okay"; + force-bus-format = ; + force-output; + force_timing{ + clock-frequency = <66000000>; + hactive = <800>; + vactive = <1280>; + hback-porch = <30>; + hfront-porch = <30>; + vback-porch = <3>; + vfront-porch = <3>; + hsync-len = <10>; + vsync-len = <2>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2lvds-dual-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2lvds-dual-ddr4-v10.dts new file mode 100644 index 000000000000..b48bb44cc2bf --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-hdmi2lvds-dual-ddr4-v10.dts @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +#include +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" + +&backlight { + enable-gpios = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>; +}; + +>1x { + status = "disabled"; +}; + +&i2c2_rk628 { + panel-backlight = <&backlight>; + panel-power-supply = <&vcc3v3_lcd0_n>; + panel-reset-delay-ms = <10>; + panel-enable-delay-ms = <10>; + panel-prepare-delay-ms = <60>; + panel-unprepare-delay-ms = <10>; + panel-disable-delay-ms = <60>; + + rk628,hdmi-in; + rk628-lvds { + /* "jeida_18","vesa_24","vesa_18" */ + bus-format = "vesa_24"; + + /* "dual_link_odd_even_pixels" + * "dual_link_even_odd_pixels" + * "dual_link_left_right_pixels" + * "dual_link_right_left_pixels" + */ + link-type = "dual_link_even_odd_pixels"; + status = "okay"; + }; + + display-timings { + src-timing { + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hback-porch = <148>; + hfront-porch = <88>; + vback-porch = <36>; + vfront-porch = <4>; + hsync-len = <44>; + vsync-len = <5>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; + + dst-timing { + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hback-porch = <148>; + hfront-porch = <88>; + vback-porch = <36>; + vfront-porch = <4>; + hsync-len = <44>; + vsync-len = <5>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; +}; + +&hdmi { + status = "okay"; + force-bus-format = ; + force-output; + force_timing{ + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hback-porch = <148>; + hfront-porch = <88>; + vback-porch = <36>; + vfront-porch = <4>; + hsync-len = <44>; + vsync-len = <5>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; +}; + +&hdmi_in_vp0 { + status = "okay"; +}; + +&hdmi_in_vp1 { + status = "disabled"; +}; + +&route_hdmi { + status = "okay"; + force-bus-format = ; + force-output; + force_timing{ + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hback-porch = <148>; + hfront-porch = <88>; + vback-porch = <36>; + vfront-porch = <4>; + hsync-len = <44>; + vsync-len = <5>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + pixelclk-active = <0>; + }; +}; + +&vcc3v3_lcd0_n { + gpio = <&gpio2 RK_PC6 GPIO_ACTIVE_HIGH>; + enable-active-high; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2dsi-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2dsi-ddr4-v10.dts new file mode 100644 index 000000000000..804640323de1 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2dsi-ddr4-v10.dts @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" + +/ { + panel@0 { + compatible = "simple-panel"; + + disp_timings3: display-timings { + native-mode = <&rgb2dsi_timing>; + rgb2dsi_timing: timing0 { + clock-frequency = <132000000>; + hactive = <1080>; + vactive = <1920>; + hfront-porch = <15>; + hsync-len = <2>; + hback-porch = <30>; + vfront-porch = <15>; + vsync-len = <2>; + vback-porch = <15>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <1>; + }; + }; + + port { + panel_in_rgb: endpoint { + remote-endpoint = <&rgb_out_panel>; + }; + }; + }; + +}; + +>1x { + status = "okay"; + power-supply = <&vcc3v3_lcd0_n>; + goodix,rst-gpio = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>; + goodix,irq-gpio = <&gpio0 RK_PA4 IRQ_TYPE_LEVEL_LOW>; +}; + +&i2c2_rk628 { + panel-backlight = <&backlight>; + panel-power-supply = <&vcc3v3_lcd0_n>; + panel-reset-delay-ms = <10>; + panel-enable-delay-ms = <10>; + panel-prepare-delay-ms = <60>; + panel-unprepare-delay-ms = <10>; + panel-disable-delay-ms = <60>; + + rk628,rgb-in; + rk628-dsi { + //rockchip,dual-channel; + dsi,eotp; + dsi,video-mode; + dsi,format = "rgb888"; + dsi,lanes = <4>; + status = "okay"; + + rk628-panel { + panel-init-sequence = [ + 23 00 02 FE 21 + 23 00 02 04 00 + 23 00 02 00 64 + 23 00 02 2A 00 + 23 00 02 26 64 + 23 00 02 54 00 + 23 00 02 50 64 + 23 00 02 7B 00 + 23 00 02 77 64 + 23 00 02 A2 00 + 23 00 02 9D 64 + 23 00 02 C9 00 + 23 00 02 C5 64 + 23 00 02 01 71 + 23 00 02 27 71 + 23 00 02 51 71 + 23 00 02 78 71 + 23 00 02 9E 71 + 23 00 02 C6 71 + 23 00 02 02 89 + 23 00 02 28 89 + 23 00 02 52 89 + 23 00 02 79 89 + 23 00 02 9F 89 + 23 00 02 C7 89 + 23 00 02 03 9E + 23 00 02 29 9E + 23 00 02 53 9E + 23 00 02 7A 9E + 23 00 02 A0 9E + 23 00 02 C8 9E + 23 00 02 09 00 + 23 00 02 05 B0 + 23 00 02 31 00 + 23 00 02 2B B0 + 23 00 02 5A 00 + 23 00 02 55 B0 + 23 00 02 80 00 + 23 00 02 7C B0 + 23 00 02 A7 00 + 23 00 02 A3 B0 + 23 00 02 CE 00 + 23 00 02 CA B0 + 23 00 02 06 C0 + 23 00 02 2D C0 + 23 00 02 56 C0 + 23 00 02 7D C0 + 23 00 02 A4 C0 + 23 00 02 CB C0 + 23 00 02 07 CF + 23 00 02 2F CF + 23 00 02 58 CF + 23 00 02 7E CF + 23 00 02 A5 CF + 23 00 02 CC CF + 23 00 02 08 DD + 23 00 02 30 DD + 23 00 02 59 DD + 23 00 02 7F DD + 23 00 02 A6 DD + 23 00 02 CD DD + 23 00 02 0E 15 + 23 00 02 0A E9 + 23 00 02 36 15 + 23 00 02 32 E9 + 23 00 02 5F 15 + 23 00 02 5B E9 + 23 00 02 85 15 + 23 00 02 81 E9 + 23 00 02 AD 15 + 23 00 02 A9 E9 + 23 00 02 D3 15 + 23 00 02 CF E9 + 23 00 02 0B 14 + 23 00 02 33 14 + 23 00 02 5C 14 + 23 00 02 82 14 + 23 00 02 AA 14 + 23 00 02 D0 14 + 23 00 02 0C 36 + 23 00 02 34 36 + 23 00 02 5D 36 + 23 00 02 83 36 + 23 00 02 AB 36 + 23 00 02 D1 36 + 23 00 02 0D 6B + 23 00 02 35 6B + 23 00 02 5E 6B + 23 00 02 84 6B + 23 00 02 AC 6B + 23 00 02 D2 6B + 23 00 02 13 5A + 23 00 02 0F 94 + 23 00 02 3B 5A + 23 00 02 37 94 + 23 00 02 64 5A + 23 00 02 60 94 + 23 00 02 8A 5A + 23 00 02 86 94 + 23 00 02 B2 5A + 23 00 02 AE 94 + 23 00 02 D8 5A + 23 00 02 D4 94 + 23 00 02 10 D1 + 23 00 02 38 D1 + 23 00 02 61 D1 + 23 00 02 87 D1 + 23 00 02 AF D1 + 23 00 02 D5 D1 + 23 00 02 11 04 + 23 00 02 39 04 + 23 00 02 62 04 + 23 00 02 88 04 + 23 00 02 B0 04 + 23 00 02 D6 04 + 23 00 02 12 05 + 23 00 02 3A 05 + 23 00 02 63 05 + 23 00 02 89 05 + 23 00 02 B1 05 + 23 00 02 D7 05 + 23 00 02 18 AA + 23 00 02 14 36 + 23 00 02 42 AA + 23 00 02 3D 36 + 23 00 02 69 AA + 23 00 02 65 36 + 23 00 02 8F AA + 23 00 02 8B 36 + 23 00 02 B7 AA + 23 00 02 B3 36 + 23 00 02 DD AA + 23 00 02 D9 36 + 23 00 02 15 74 + 23 00 02 3F 74 + 23 00 02 66 74 + 23 00 02 8C 74 + 23 00 02 B4 74 + 23 00 02 DA 74 + 23 00 02 16 9F + 23 00 02 40 9F + 23 00 02 67 9F + 23 00 02 8D 9F + 23 00 02 B5 9F + 23 00 02 DB 9F + 23 00 02 17 DC + 23 00 02 41 DC + 23 00 02 68 DC + 23 00 02 8E DC + 23 00 02 B6 DC + 23 00 02 DC DC + 23 00 02 1D FF + 23 00 02 19 03 + 23 00 02 47 FF + 23 00 02 43 03 + 23 00 02 6E FF + 23 00 02 6A 03 + 23 00 02 94 FF + 23 00 02 90 03 + 23 00 02 BC FF + 23 00 02 B8 03 + 23 00 02 E2 FF + 23 00 02 DE 03 + 23 00 02 1A 35 + 23 00 02 44 35 + 23 00 02 6B 35 + 23 00 02 91 35 + 23 00 02 B9 35 + 23 00 02 DF 35 + 23 00 02 1B 45 + 23 00 02 45 45 + 23 00 02 6C 45 + 23 00 02 92 45 + 23 00 02 BA 45 + 23 00 02 E0 45 + 23 00 02 1C 55 + 23 00 02 46 55 + 23 00 02 6D 55 + 23 00 02 93 55 + 23 00 02 BB 55 + 23 00 02 E1 55 + 23 00 02 22 FF + 23 00 02 1E 68 + 23 00 02 4C FF + 23 00 02 48 68 + 23 00 02 73 FF + 23 00 02 6F 68 + 23 00 02 99 FF + 23 00 02 95 68 + 23 00 02 C1 FF + 23 00 02 BD 68 + 23 00 02 E7 FF + 23 00 02 E3 68 + 23 00 02 1F 7E + 23 00 02 49 7E + 23 00 02 70 7E + 23 00 02 96 7E + 23 00 02 BE 7E + 23 00 02 E4 7E + 23 00 02 20 97 + 23 00 02 4A 97 + 23 00 02 71 97 + 23 00 02 97 97 + 23 00 02 BF 97 + 23 00 02 E5 97 + 23 00 02 21 B5 + 23 00 02 4B B5 + 23 00 02 72 B5 + 23 00 02 98 B5 + 23 00 02 C0 B5 + 23 00 02 E6 B5 + 23 00 02 25 F0 + 23 00 02 23 E8 + 23 00 02 4F F0 + 23 00 02 4D E8 + 23 00 02 76 F0 + 23 00 02 74 E8 + 23 00 02 9C F0 + 23 00 02 9A E8 + 23 00 02 C4 F0 + 23 00 02 C2 E8 + 23 00 02 EA F0 + 23 00 02 E8 E8 + 23 00 02 24 FF + 23 00 02 4E FF + 23 00 02 75 FF + 23 00 02 9B FF + 23 00 02 C3 FF + 23 00 02 E9 FF + 23 00 02 FE 3D + 23 00 02 00 04 + 23 00 02 FE 23 + 23 00 02 08 82 + 23 00 02 0A 00 + 23 00 02 0B 00 + 23 00 02 0C 01 + 23 00 02 16 00 + 23 00 02 18 02 + 23 00 02 1B 04 + 23 00 02 19 04 + 23 00 02 1C 81 + 23 00 02 1F 00 + 23 00 02 20 03 + 23 00 02 23 04 + 23 00 02 21 01 + 23 00 02 54 63 + 23 00 02 55 54 + 23 00 02 6E 45 + 23 00 02 6D 36 + 23 00 02 FE 3D + 23 00 02 55 78 + 23 00 02 FE 20 + 23 00 02 26 30 + 23 00 02 FE 3D + 23 00 02 20 71 + 23 00 02 50 8F + 23 00 02 51 8F + 23 00 02 FE 00 + 23 00 02 35 00 + 05 78 01 11 + 05 00 01 29 + ]; + + panel-exit-sequence = [ + 05 00 01 28 + 05 00 01 10 + ]; + }; + }; + + display-timings { + src-timing { + clock-frequency = <132000000>; + hactive = <1080>; + vactive = <1920>; + hback-porch = <30>; + hfront-porch = <15>; + vback-porch = <15>; + vfront-porch = <15>; + hsync-len = <2>; + vsync-len = <2>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + + dst-timing { + clock-frequency = <132000000>; + hactive = <1080>; + vactive = <1920>; + hback-porch = <30>; + hfront-porch = <15>; + vback-porch = <15>; + vfront-porch = <15>; + hsync-len = <2>; + vsync-len = <2>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; +}; + +&route_rgb { + status = "okay"; +}; + +&rgb_in_vp2 { + status = "okay"; +}; + +&rgb { + status = "okay"; + + ports { + port@1 { + reg = <1>; + + rgb_out_panel: endpoint { + remote-endpoint = <&panel_in_rgb>; + }; + }; + }; +}; + +&touch_gpio { + rockchip,pins = <0 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>, + <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2gvi-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2gvi-ddr4-v10.dts new file mode 100644 index 000000000000..9b47d9eda21a --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2gvi-ddr4-v10.dts @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" + +/ { + panel@0 { + compatible = "simple-panel"; + + disp_timings3: display-timings { + native-mode = <&rgb2lvds_timing>; + rgb2lvds_timing: timing0 { + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hback-porch = <148>; + hfront-porch = <88>; + vback-porch = <36>; + vfront-porch = <4>; + hsync-len = <44>; + vsync-len = <5>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; + + port { + panel_in_rgb: endpoint { + remote-endpoint = <&rgb_out_panel>; + }; + }; + }; + +}; + +&backlight { + enable-gpios = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>; +}; + +>1x { + status = "disabled"; +}; + +&i2c2_rk628 { + panel-backlight = <&backlight>; + panel-power-supply = <&vcc3v3_lcd0_n>; + panel-reset-delay-ms = <10>; + panel-enable-delay-ms = <100>; + panel-prepare-delay-ms = <60>; + panel-unprepare-delay-ms = <10>; + panel-disable-delay-ms = <60>; + pinctrl-names = "default"; + pinctrl-0 = <&rk628_reset &refclk_pins>; + assigned-clocks = <&pmucru CLK_WIFI>; + assigned-clock-rates = <24000000>; + clocks = <&pmucru CLK_WIFI>; + clock-names = "soc_24M"; + + rk628,rgb-in; + rk628-gvi { + /* "rgb666" + * "rgb888" + * "rgb101010" + * "yuyv8" + * "yuyv10" + */ + bus-format = "rgb888"; + gvi,lanes = <8>; + //"rockchip,division-mode"; + //"rockchip, gvi-frm-rst"; + status = "okay"; + }; + + display-timings { + src-timing { + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hback-porch = <148>; + hfront-porch = <88>; + vback-porch = <36>; + vfront-porch = <4>; + hsync-len = <44>; + vsync-len = <5>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + + dst-timing { + clock-frequency = <594000000>; + hactive = <3840>; + vactive = <2160>; + hback-porch = <296>; + hfront-porch = <176>; + vback-porch = <72>; + vfront-porch = <8>; + hsync-len = <88>; + vsync-len = <10>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; +}; + +&route_rgb { + status = "okay"; +}; + +&rgb_in_vp2 { + status = "okay"; +}; + +&rgb { + status = "okay"; + + ports { + port@1 { + reg = <1>; + + rgb_out_panel: endpoint { + remote-endpoint = <&panel_in_rgb>; + }; + }; + }; +}; + +&vcc3v3_lcd0_n { + gpio = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>; + enable-active-high; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2hdmi-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2hdmi-ddr4-v10.dts new file mode 100644 index 000000000000..83b62f996aff --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2hdmi-ddr4-v10.dts @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" + +&backlight { + enable-gpios = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>; +}; + +>1x { + status = "disabled"; +}; + +&i2c2_rk628 { + assigned-clocks = <&pmucru CLK_WIFI>; + assigned-clock-rates = <24000000>; + clocks = <&pmucru CLK_WIFI>; + clock-names = "soc_24M"; + pinctrl-names = "default"; + pinctrl-0 = <&rk628_reset &refclk_pins>; + + rk628,rgb-in; + rk628,hdmi-out; + status = "okay"; + + port { + rgb_in_hdmi: endpoint { + remote-endpoint = <&rgb_out_hdmi>; + }; + }; +}; + +&i2c5 { + status = "disabled"; +}; + +&i2s2_2ch { + status = "okay"; +}; + +&route_rgb { + status = "disabled"; +}; + +&rgb_in_vp2 { + status = "okay"; +}; + +&rgb { + status = "okay"; + + ports { + port@1 { + reg = <1>; + + rgb_out_hdmi: endpoint { + remote-endpoint = <&rgb_in_hdmi>; + }; + }; + }; +}; + +&rk628_sound { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2lvds-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2lvds-ddr4-v10.dts new file mode 100644 index 000000000000..28adb73ce751 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2lvds-ddr4-v10.dts @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" + +/ { + panel@0 { + compatible = "simple-panel"; + + disp_timings3: display-timings { + native-mode = <&rgb2lvds_timing>; + rgb2lvds_timing: timing0 { + clock-frequency = <66000000>; + hactive = <800>; + vactive = <1280>; + hback-porch = <30>; + hfront-porch = <30>; + vback-porch = <3>; + vfront-porch = <3>; + hsync-len = <4>; + vsync-len = <2>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; + + port { + panel_in_rgb: endpoint { + remote-endpoint = <&rgb_out_panel>; + }; + }; + }; + +}; + +&backlight { + pwms = <&pwm4 0 25000 PWM_POLARITY_INVERTED>; +}; + +>1x { + status = "disabled"; +}; + +&i2c2_rk628 { + panel-backlight = <&backlight>; + panel-power-supply = <&vcc3v3_lcd0_n>; + panel-reset-delay-ms = <10>; + panel-enable-delay-ms = <10>; + panel-prepare-delay-ms = <60>; + panel-unprepare-delay-ms = <10>; + panel-disable-delay-ms = <60>; + + rk628,rgb-in; + rk628-lvds { + /* "jeida_18","vesa_24","vesa_18" */ + bus-format = "jeida_24"; + + /* "dual_link_odd_even_pixels" + * "dual_link_even_odd_pixels" + * "dual_link_left_right_pixels" + * "dual_link_right_left_pixels" + */ + link-type = "single_link"; + status = "okay"; + }; + + display-timings { + src-timing { + clock-frequency = <66000000>; + hactive = <800>; + vactive = <1280>; + hback-porch = <30>; + hfront-porch = <30>; + vback-porch = <3>; + vfront-porch = <3>; + hsync-len = <4>; + vsync-len = <2>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + + dst-timing { + clock-frequency = <66000000>; + hactive = <800>; + vactive = <1280>; + hback-porch = <30>; + hfront-porch = <30>; + vback-porch = <3>; + vfront-porch = <3>; + hsync-len = <4>; + vsync-len = <2>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; +}; + +&route_rgb { + status = "okay"; +}; + +&rgb_in_vp2 { + status = "okay"; +}; + +&rgb { + status = "okay"; + + ports { + port@1 { + reg = <1>; + + rgb_out_panel: endpoint { + remote-endpoint = <&panel_in_rgb>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2lvds-dual-ddr4-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2lvds-dual-ddr4-v10.dts new file mode 100644 index 000000000000..65321128ec17 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-evb-rk628-rgb2lvds-dual-ddr4-v10.dts @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +#include "rk3568-evb-rk628-ddr4-v10.dtsi" +#include "rk3568-android.dtsi" + +/ { + panel@0 { + compatible = "simple-panel"; + + disp_timings3: display-timings { + native-mode = <&rgb2lvds_timing>; + rgb2lvds_timing: timing0 { + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hback-porch = <96>; + hfront-porch = <120>; + vback-porch = <8>; + vfront-porch = <33>; + hsync-len = <64>; + vsync-len = <4>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; + + port { + panel_in_rgb: endpoint { + remote-endpoint = <&rgb_out_panel>; + }; + }; + }; + +}; + +&backlight { + enable-gpios = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>; + pwms = <&pwm4 0 25000 PWM_POLARITY_INVERTED>; +}; + +>1x { + status = "disabled"; +}; + +&i2c2_rk628 { + panel-backlight = <&backlight>; + panel-power-supply = <&vcc3v3_lcd0_n>; + panel-reset-delay-ms = <10>; + panel-enable-delay-ms = <10>; + panel-prepare-delay-ms = <60>; + panel-unprepare-delay-ms = <10>; + panel-disable-delay-ms = <60>; + + rk628,rgb-in; + rk628-lvds { + /* "jeida_18","vesa_24","vesa_18" */ + bus-format = "vesa_24"; + + /* "dual_link_odd_even_pixels" + * "dual_link_even_odd_pixels" + * "dual_link_left_right_pixels" + * "dual_link_right_left_pixels" + */ + link-type = "dual_link_even_odd_pixels"; + status = "okay"; + }; + + display-timings { + src-timing { + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hback-porch = <96>; + hfront-porch = <120>; + vback-porch = <8>; + vfront-porch = <33>; + hsync-len = <64>; + vsync-len = <4>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + + dst-timing { + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hback-porch = <96>; + hfront-porch = <120>; + vback-porch = <8>; + vfront-porch = <33>; + hsync-len = <64>; + vsync-len = <4>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <0>; + }; + }; +}; + +&route_rgb { + status = "okay"; +}; + +&rgb_in_vp2 { + status = "okay"; +}; + +&rgb { + status = "okay"; + + ports { + port@1 { + reg = <1>; + + rgb_out_panel: endpoint { + remote-endpoint = <&panel_in_rgb>; + }; + }; + }; +}; + +&vcc3v3_lcd0_n { + gpio = <&gpio2 RK_PC6 GPIO_ACTIVE_HIGH>; + enable-active-high; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-rk628-hdmi2csi.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-rk628-hdmi2csi.dts index 992234359490..8aa1a74159ff 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-rk628-hdmi2csi.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10-rk628-hdmi2csi.dts @@ -31,40 +31,6 @@ &csi2_dphy0 { status = "okay"; - ports { - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - #address-cells = <1>; - #size-cells = <0>; - - hdmi_mipi2_in: endpoint@1 { - reg = <1>; - remote-endpoint = <&hdmiin_out1>; - 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_dphy0_hw { - status = "okay"; -}; - -&csi2_dcphy0 { - status = "okay"; - ports { #address-cells = <1>; #size-cells = <0>; @@ -92,7 +58,46 @@ }; }; +&csi2_dphy0_hw { + status = "okay"; +}; + +&csi2_dphy1 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + hdmi_mipi2_in: endpoint@1 { + reg = <1>; + remote-endpoint = <&hdmiin_out1>; + 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_hw { + status = "okay"; +}; + &i2c3 { + clock-frequency = <400000>; status = "okay"; rk628_csi_1: rk628_csi_1@50 { @@ -113,6 +118,15 @@ rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "HDMI-MIPI2"; rockchip,camera-module-lens-name = "RK628-CSI"; + + multi-dev-info { + dev-idx-l = <2>; + dev-idx-r = <4>; + combine-idx = <2>; + pixel-offset = <0>; + dev-num = <2>; + }; + port { hdmiin_out1: endpoint { remote-endpoint = <&hdmi_mipi2_in>; @@ -123,6 +137,7 @@ }; &i2c5 { + clock-frequency = <400000>; status = "okay"; rk628_csi: rk628_csi@50 { @@ -143,6 +158,15 @@ rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "HDMI-MIPI"; rockchip,camera-module-lens-name = "RK628-CSI"; + + multi-dev-info { + dev-idx-l = <0>; + dev-idx-r = <1>; + combine-idx = <0>; + pixel-offset = <0>; + dev-num = <2>; + }; + port { hdmiin_out0: endpoint { remote-endpoint = <&hdmi_mipi0_in>; @@ -156,6 +180,10 @@ status = "okay"; }; +&mipi_dcphy1 { + status = "okay"; +}; + &mipi0_csi2 { status = "okay"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb7-v11-rk628-hdmi2csi.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb7-v11-rk628-hdmi2csi.dts new file mode 100644 index 000000000000..a0de491c2efc --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3588-evb7-v11-rk628-hdmi2csi.dts @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + * + */ +/dts-v1/; + +#include "rk3588-evb7-v11.dtsi" +#include "rk3588-android.dtsi" + +/ { + model = "Rockchip RK3588 EVB7 V11 Board + Rockchip RK628 HDMI to MIPI Extboard"; + compatible = "rockchip,rk3588-evb7-v11", "rockchip,rk3588"; + + vcc_mipicsi0: vcc-mipicsi0-regulator { + /delete-property/ gpio; + /delete-property/ pinctrl-0; + }; + + vcc_mipicsi1: vcc-mipicsi1-regulator { + /delete-property/ gpio; + /delete-property/ pinctrl-0; + }; + + vcc_mipidcphy0: vcc-mipidcphy0-regulator { + /delete-property/ gpio; + /delete-property/ pinctrl-0; + }; +}; + +&csi2_dphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + hdmi_mipi2_in: endpoint@1 { + reg = <1>; + remote-endpoint = <&hdmiin_out1>; + 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_dphy0_hw { + status = "okay"; +}; + +&csi2_dphy1_hw { + status = "okay"; +}; + +&i2c3 { + status = "okay"; + clock-frequency = <400000>; + + rk628_csi: rk628_csi@50 { + reg = <0x50>; + compatible = "rockchip,rk628-csi-v4l2"; + status = "okay"; + power-domains = <&power RK3588_PD_VI>; + pinctrl-names = "default"; + pinctrl-0 = <&rk628_pin>; + interrupt-parent = <&gpio1>; + interrupts = ; + enable-gpios = <&gpio1 RK_PA7 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_HIGH>; + plugin-det-gpios = <&gpio2 RK_PB6 GPIO_ACTIVE_LOW>; + continues-clk = <1>; + + rockchip,camera-module-index = <0>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "HDMI-MIPI2"; + rockchip,camera-module-lens-name = "RK628-CSI"; + + multi-dev-info { + dev-idx-l = <2>; + dev-idx-r = <4>; + combine-idx = <2>; + pixel-offset = <0>; + dev-num = <2>; + }; + + port { + hdmiin_out1: endpoint { + remote-endpoint = <&hdmi_mipi2_in>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy0 { + status = "okay"; +}; + +&mipi_dcphy1 { + status = "okay"; +}; + +&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 { + status = "okay"; +}; + +&rkcif_mipi_lvds2 { + status = "okay"; + + port { + cif_mipi_in2: endpoint { + remote-endpoint = <&mipi2_csi2_output>; + }; + }; +}; + +&rkcif_mmu { + status = "okay"; +}; + +&pinctrl { + hdmiin { + rk628_pin: rk628-pin { + rockchip,pins = <1 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>, + <1 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>, + <1 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>, + <2 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; diff --git a/arch/arm64/configs/rockchip_defconfig b/arch/arm64/configs/rockchip_defconfig index b52af5189f53..c04c162646ff 100644 --- a/arch/arm64/configs/rockchip_defconfig +++ b/arch/arm64/configs/rockchip_defconfig @@ -293,6 +293,8 @@ CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_BLK_DEV_NVME=y +CONFIG_RK628_MISC=y +CONFIG_RK628_MISC_HDMITX=y CONFIG_LT7911D_FB_NOTIFIER=y CONFIG_SRAM=y CONFIG_UID_SYS_STATS=y @@ -556,7 +558,6 @@ CONFIG_ROCKCHIP_THERMAL=y CONFIG_WATCHDOG=y CONFIG_DW_WATCHDOG=y CONFIG_MFD_RK618=y -CONFIG_MFD_RK628=y CONFIG_MFD_RK630_I2C=y CONFIG_MFD_RK806_I2C=y CONFIG_MFD_RK806_SPI=y @@ -639,7 +640,6 @@ CONFIG_ROCKCHIP_LVDS=y CONFIG_ROCKCHIP_RGB=y CONFIG_ROCKCHIP_DW_HDCP2=y CONFIG_DRM_ROCKCHIP_RK618=y -CONFIG_DRM_ROCKCHIP_RK628=y CONFIG_DRM_PANEL_SIMPLE=y CONFIG_DRM_PANEL_MAXIM_MAX96752F=y CONFIG_DRM_PANEL_MAXIM_MAX96772=y diff --git a/drivers/media/i2c/rk628/Makefile b/drivers/media/i2c/rk628/Makefile index b94308c1ee74..44d4388bf547 100644 --- a/drivers/media/i2c/rk628/Makefile +++ b/drivers/media/i2c/rk628/Makefile @@ -2,7 +2,8 @@ # # Makefile for the Rockchip RK628 display bridge driver. # -video-rk628-objs := rk628.o rk628_cru.o rk628_hdmirx.o rk628_dsi.o rk628_combrxphy.o rk628_combtxphy.o +video-rk628-objs := rk628.o rk628_cru.o rk628_hdmirx.o rk628_dsi.o rk628_combrxphy.o \ + rk628_combtxphy.o rk628_mipi_dphy.o rk628_post_process.o obj-$(CONFIG_VIDEO_RK628) += video-rk628.o rk628-csi-objs := rk628_csi_v4l2.o diff --git a/drivers/media/i2c/rk628/rk628.c b/drivers/media/i2c/rk628/rk628.c index 3a0975ac6b59..8236eaef9320 100644 --- a/drivers/media/i2c/rk628/rk628.c +++ b/drivers/media/i2c/rk628/rk628.c @@ -22,6 +22,7 @@ static const struct regmap_range rk628_cru_readable_ranges[] = { regmap_reg_range(CRU_CPLL_CON0, CRU_CPLL_CON4), regmap_reg_range(CRU_GPLL_CON0, CRU_GPLL_CON4), + regmap_reg_range(CRU_APLL_CON0, CRU_APLL_CON4), regmap_reg_range(CRU_MODE_CON00, CRU_MODE_CON00), regmap_reg_range(CRU_CLKSEL_CON00, CRU_CLKSEL_CON21), regmap_reg_range(CRU_GATE_CON00, CRU_GATE_CON05), @@ -46,12 +47,12 @@ static const struct regmap_access_table rk628_combrxphy_readable_table = { }; static const struct regmap_range rk628_hdmirx_readable_ranges[] = { - regmap_reg_range(HDMI_RX_HDMI_SETUP_CTRL, HDMI_RX_HDMI_SETUP_CTRL), + regmap_reg_range(HDMI_RX_HDMI_SETUP_CTRL, HDMI_RX_HDMI_TIMER_CTRL), regmap_reg_range(HDMI_RX_HDMI_PCB_CTRL, HDMI_RX_HDMI_PCB_CTRL), regmap_reg_range(HDMI_RX_HDMI_MODE_RECOVER, HDMI_RX_HDMI_ERROR_PROTECT), regmap_reg_range(HDMI_RX_HDMI_SYNC_CTRL, HDMI_RX_HDMI_CKM_RESULT), regmap_reg_range(HDMI_RX_HDMI_RESMPL_CTRL, HDMI_RX_HDMI_RESMPL_CTRL), - regmap_reg_range(HDMI_VM_CFG_CH2, HDMI_VM_CFG_CH2), + regmap_reg_range(HDMI_VM_CFG_CH0_1, HDMI_VM_CFG_CH2), regmap_reg_range(HDMI_RX_HDCP_CTRL, HDMI_RX_HDCP_SETTINGS), regmap_reg_range(HDMI_RX_HDCP_KIDX, HDMI_RX_HDCP_KIDX), regmap_reg_range(HDMI_RX_HDCP_DBG, HDMI_RX_HDCP_AN0), @@ -67,6 +68,7 @@ static const struct regmap_range rk628_hdmirx_readable_ranges[] = { regmap_reg_range(HDMI_RX_AUD_CHEXTR_CTRL, HDMI_RX_AUD_PAO_CTRL), regmap_reg_range(HDMI_RX_AUD_FIFO_STS, HDMI_RX_AUD_FIFO_STS), regmap_reg_range(HDMI_RX_AUDPLL_GEN_CTS, HDMI_RX_AUDPLL_GEN_N), + regmap_reg_range(HDMI_RX_I2CM_PHYG3_DATAI, HDMI_RX_I2CM_PHYG3_DATAI), regmap_reg_range(HDMI_RX_PDEC_CTRL, HDMI_RX_PDEC_CTRL), regmap_reg_range(HDMI_RX_PDEC_AUDIODET_CTRL, HDMI_RX_PDEC_AUDIODET_CTRL), regmap_reg_range(HDMI_RX_PDEC_ERR_FILTER, HDMI_RX_PDEC_ASP_CTRL), @@ -76,8 +78,9 @@ static const struct regmap_range rk628_hdmirx_readable_ranges[] = { regmap_reg_range(HDMI_RX_PDEC_AIF_CTRL, HDMI_RX_PDEC_AIF_PB0), regmap_reg_range(HDMI_RX_PDEC_AVI_PB, HDMI_RX_PDEC_AVI_PB), regmap_reg_range(HDMI_RX_HDMI20_CONTROL, HDMI_RX_CHLOCK_CONFIG), - regmap_reg_range(HDMI_RX_SCDC_REGS1, HDMI_RX_SCDC_REGS2), + regmap_reg_range(HDMI_RX_SCDC_REGS0, HDMI_RX_SCDC_REGS2), regmap_reg_range(HDMI_RX_SCDC_WRDATA0, HDMI_RX_SCDC_WRDATA0), + regmap_reg_range(HDMI_RX_HDMI20_STATUS, HDMI_RX_HDMI20_STATUS), regmap_reg_range(HDMI_RX_PDEC_ISTS, HDMI_RX_PDEC_IEN), regmap_reg_range(HDMI_RX_AUD_FIFO_ISTS, HDMI_RX_AUD_FIFO_IEN), regmap_reg_range(HDMI_RX_MD_ISTS, HDMI_RX_MD_IEN), @@ -112,6 +115,7 @@ static const struct regmap_range rk628_csi_readable_ranges[] = { regmap_reg_range(CSITX_CONFIG_DONE, CSITX_CSITX_VERSION), regmap_reg_range(CSITX_SYS_CTRL0_IMD, CSITX_TIMING_HPW_PADDING_NUM), regmap_reg_range(CSITX_VOP_PATH_CTRL, CSITX_VOP_PATH_CTRL), + regmap_reg_range(CSITX_VOP_FILTER_CTRL, CSITX_VOP_FILTER_CTRL), regmap_reg_range(CSITX_VOP_PATH_PKT_CTRL, CSITX_VOP_PATH_PKT_CTRL), regmap_reg_range(CSITX_CSITX_STATUS0, CSITX_LPDT_DATA_IMD), regmap_reg_range(CSITX_DPHY_CTRL, CSITX_DPHY_CTRL), @@ -122,6 +126,21 @@ static const struct regmap_access_table rk628_csi_readable_table = { .n_yes_ranges = ARRAY_SIZE(rk628_csi_readable_ranges), }; +static const struct regmap_range rk628_csi1_readable_ranges[] = { + regmap_reg_range(CSITX1_CONFIG_DONE, CSITX1_CSITX_VERSION), + regmap_reg_range(CSITX1_SYS_CTRL0_IMD, CSITX1_TIMING_HPW_PADDING_NUM), + regmap_reg_range(CSITX1_VOP_PATH_CTRL, CSITX1_VOP_PATH_CTRL), + regmap_reg_range(CSITX1_VOP_FILTER_CTRL, CSITX1_VOP_FILTER_CTRL), + regmap_reg_range(CSITX1_VOP_PATH_PKT_CTRL, CSITX1_VOP_PATH_PKT_CTRL), + regmap_reg_range(CSITX1_CSITX_STATUS0, CSITX1_LPDT_DATA_IMD), + regmap_reg_range(CSITX1_DPHY_CTRL, CSITX1_DPHY_CTRL), +}; + +static const struct regmap_access_table rk628_csi1_readable_table = { + .yes_ranges = rk628_csi1_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(rk628_csi1_readable_ranges), +}; + static const struct regmap_range rk628_dsi0_readable_ranges[] = { regmap_reg_range(DSI0_BASE, DSI0_BASE + DSI_MAX_REGISTER), }; @@ -231,8 +250,192 @@ static const struct regmap_config rk628_regmap_config[RK628_DEV_MAX] = { .val_format_endian = REGMAP_ENDIAN_LITTLE, .rd_table = &rk628_csi_readable_table, }, + [RK628_DEV_CSI1] = { + .name = "csi1", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = CSI1_MAX_REGISTER, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .rd_table = &rk628_csi1_readable_table, + }, }; +int rk628_media_i2c_write(struct rk628 *rk628, u32 reg, u32 val) +{ + int region = (reg >> 16) & 0xff; + int ret = 0; + + if (region >= RK628_DEV_MAX) { + dev_err(rk628->dev, + "%s: i2c err: invalid arguments, out of register range\n", __func__); + return -EINVAL; + } + + ret = regmap_write(rk628->regmap[region], reg, val); + if (ret < 0) + dev_err(rk628->dev, + "%s: i2c err reg=0x%x, val=0x%x, ret=%d\n", __func__, reg, val, ret); + + return ret; +} +EXPORT_SYMBOL(rk628_media_i2c_write); + +int rk628_media_i2c_read(struct rk628 *rk628, u32 reg, u32 *val) +{ + int region = (reg >> 16) & 0xff; + int ret = 0; + + if (region >= RK628_DEV_MAX) { + dev_err(rk628->dev, + "%s: i2c err: invalid arguments, out of register range\n", __func__); + return -EINVAL; + } + + ret = regmap_read(rk628->regmap[region], reg, val); + if (ret < 0) + dev_err(rk628->dev, + "%s: i2c err reg=0x%x, val=0x%x ret=%d\n", __func__, reg, *val, ret); + + return ret; +} +EXPORT_SYMBOL(rk628_media_i2c_read); + +int rk628_media_i2c_update_bits(struct rk628 *rk628, u32 reg, u32 mask, + u32 val) +{ + int region = (reg >> 16) & 0xff; + + if (region >= RK628_DEV_MAX) { + dev_err(rk628->dev, + "%s: i2c err: invalid arguments, out of register range\n", __func__); + return -EINVAL; + } + + return regmap_update_bits(rk628->regmap[region], reg, mask, val); +} +EXPORT_SYMBOL(rk628_media_i2c_update_bits); + +static int rk628_reg_show(struct seq_file *s, void *v) +{ + const struct regmap_config *reg; + struct rk628 *rk628 = s->private; + unsigned int i, j; + u32 val; + + seq_printf(s, "rk628_%s:\n", file_dentry(s->file)->d_iname); + + for (i = 0; i < ARRAY_SIZE(rk628_regmap_config); i++) { + reg = &rk628_regmap_config[i]; + if (!reg->name) + continue; + if (!strcmp(reg->name, file_dentry(s->file)->d_iname)) + break; + } + + if (i == ARRAY_SIZE(rk628_regmap_config)) + return -ENODEV; + + /* grf */ + if (!reg->rd_table) { + for (i = 0; i <= reg->max_register; i += 4) { + rk628_i2c_read(rk628, i, &val); + if (i % 16 == 0) + seq_printf(s, "\n0x%04x:", i); + seq_printf(s, " %08x", val); + } + } else { + const struct regmap_range *range_list = reg->rd_table->yes_ranges; + const struct regmap_range *range; + int range_list_len = reg->rd_table->n_yes_ranges; + + for (i = 0; i < range_list_len; i++) { + range = &range_list[i]; + for (j = range->range_min; j <= range->range_max; j += 4) { + rk628_i2c_read(rk628, j, &val); + if (j % 16 == 0 || j == range->range_min) + seq_printf(s, "\n0x%04x:", j); + seq_printf(s, " %08x", val); + } + } + + seq_puts(s, "\n"); + } + + return 0; +} + +static ssize_t rk628_reg_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct rk628 *rk628 = file->f_path.dentry->d_inode->i_private; + u32 addr; + u32 val; + char kbuf[25]; + int ret; + + if (count >= sizeof(kbuf)) + return -ENOSPC; + + if (copy_from_user(kbuf, buf, count)) + return -EFAULT; + + kbuf[count] = '\0'; + + ret = sscanf(kbuf, "%x%x", &addr, &val); + if (ret != 2) + return -EINVAL; + + rk628_i2c_write(rk628, addr, val); + + return count; +} + +static int rk628_reg_open(struct inode *inode, struct file *file) +{ + struct rk628 *rk628 = inode->i_private; + + return single_open(file, rk628_reg_show, rk628); +} + +static const struct file_operations rk628_reg_fops = { + .owner = THIS_MODULE, + .open = rk628_reg_open, + .read = seq_read, + .write = rk628_reg_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static void rk628_debugfs_register_create(struct rk628 *rk628) +{ + const struct regmap_config *reg; + struct dentry *dir; + int i; + + dir = debugfs_create_dir("registers", rk628->debug_dir); + if (IS_ERR(dir)) + return; + + for (i = 0; i < ARRAY_SIZE(rk628_regmap_config); i++) { + reg = &rk628_regmap_config[i]; + if (!reg->name) + continue; + debugfs_create_file(reg->name, 0600, dir, rk628, &rk628_reg_fops); + } +} + +void rk628_debugfs_create(struct rk628 *rk628) +{ + rk628->debug_dir = debugfs_create_dir(dev_name(rk628->dev), debugfs_lookup("rk628", NULL)); + if (IS_ERR(rk628->debug_dir)) + return; + + rk628_debugfs_register_create(rk628); +} +EXPORT_SYMBOL(rk628_debugfs_create); + struct rk628 *rk628_i2c_register(struct i2c_client *client) { struct rk628 *rk628; @@ -259,6 +462,7 @@ struct rk628 *rk628_i2c_register(struct i2c_client *client) return NULL; } } + mutex_init(&rk628->rst_lock); return rk628; } @@ -475,6 +679,29 @@ void rk628_post_process_en(struct rk628 *rk628, } EXPORT_SYMBOL(rk628_post_process_en); +static const char * const rk628_version[] = { + "UNKNOWN", + "RK628D", + "RK628F/H", +}; + +void rk628_version_parse(struct rk628 *rk628) +{ + u32 version; + + rk628_i2c_read(rk628, GRF_SOC_VERSION, &version); + if (version == 0x20200326) + rk628->version = RK628D_VERSION; + else if (version == 0x20230321) + rk628->version = RK628F_VERSION; + else + rk628->version = RK628_UNKNOWN; + + dev_info(rk628->dev, "rk628 version is: %s (%x)\n", + rk628_version[rk628->version], version); +} +EXPORT_SYMBOL(rk628_version_parse); + MODULE_AUTHOR("Shunqing Chen "); MODULE_DESCRIPTION("Rockchip RK628 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/rk628/rk628.h b/drivers/media/i2c/rk628/rk628.h index 3f15be0ba929..6ea3ad0d6437 100644 --- a/drivers/media/i2c/rk628/rk628.h +++ b/drivers/media/i2c/rk628/rk628.h @@ -34,6 +34,8 @@ #define SW_EFUSE_HDCP_EN(x) UPDATE(x, 8, 8) #define SW_OUTPUT_MODE_MASK GENMASK(7, 3) #define SW_OUTPUT_MODE(x) UPDATE(x, 7, 3) +#define SW_OUTPUT_COMBTX_MODE_MASK GENMASK(4, 3) +#define SW_OUTPUT_COMBTX_MODE(x) UPDATE(x, 4, 3) #define SW_INPUT_MODE_MASK GENMASK(2, 0) #define SW_INPUT_MODE(x) UPDATE(x, 2, 0) #define GRF_SYSTEM_CON1 0x0004 @@ -45,6 +47,10 @@ #define GRF_GPIO_RXDDC_SDA_SEL(x) UPDATE(x, 6, 6) #define GRF_GPIO_RXDDC_SCL_SEL_MASK BIT(5) #define GRF_GPIO_RXDDC_SCL_SEL(x) UPDATE(x, 5, 5) +#define GRF_DPHY_CH1_EN_MASK BIT(1) +#define GRF_DPHY_CH1_EN(x) UPDATE(x, 1, 1) +#define GRF_AS_DSIPHY_MASK BIT(0) +#define GRF_AS_DSIPHY(x) UPDATE(x, 0, 0) #define GRF_SCALER_CON0 0x0010 #define SCL_VER_DOWN_MODE(x) HIWORD_UPDATE(x, 8, 8) #define SCL_HOR_DOWN_MODE(x) HIWORD_UPDATE(x, 7, 7) @@ -87,9 +93,13 @@ #define SW_SPLIT_MODE(x) UPDATE(x, 1, 1) #define SW_SPLIT_EN BIT(0) #define GRF_CSC_CTRL_CON 0x0038 +#define SW_Y2R_MODE(x) HIWORD_UPDATE(x, 13, 12) +#define SW_FROM_CSC_MATRIX_EN(x) HIWORD_UPDATE(x, 11, 11) #define SW_YUV2VYU_SWP(x) HIWORD_UPDATE(x, 8, 8) #define SW_R2Y_EN(x) HIWORD_UPDATE(x, 4, 4) #define SW_Y2R_EN(x) HIWORD_UPDATE(x, 0, 0) +#define SW_R2Y_CSC_MODE(x) HIWORD_UPDATE(x, 7, 6) +#define SW_Y2R_CSC_MODE(x) HIWORD_UPDATE(x, 3, 2) #define GRF_LVDS_TX_CON 0x003c #define SW_LVDS_CON_DUAL_SEL(x) HIWORD_UPDATE(x, 12, 12) #define SW_LVDS_CON_DEN_POLARITY(x) HIWORD_UPDATE(x, 11, 11) @@ -173,6 +183,17 @@ #define GRF_GPIO3A_D1_CON 0x00e4 #define GRF_GPIO3B_D_CON 0x00e8 #define GRF_GPIO_SR_CON 0x00ec +#define GRF_BG_CTRL 0x00f0 +#define BG_ENABLE_MASK GENMASK(31, 31) +#define BG_ENABLE(x) UPDATE(x, 31, 31) +#define BG_R_OR_V_MASK GENMASK(29, 20) +#define BG_R_OR_V(x) UPDATE(x, 29, 20) +#define BG_G_OR_Y_MASK GENMASK(19, 10) +#define BG_G_OR_Y(x) UPDATE(x, 19, 10) +#define BG_B_OR_U_MASK GENMASK(9, 0) +#define BG_B_OR_U(x) UPDATE(x, 9, 0) + +#define GRF_SW_HDMIRXPHY_CRTL 0x00f4 #define GRF_INTR0_EN 0x0100 #define GRF_INTR0_CLR_EN 0x0104 #define GRF_INTR0_STATUS 0x0108 @@ -190,7 +211,15 @@ #define GRF_OS_REG1 0x0144 #define GRF_OS_REG2 0x0148 #define GRF_OS_REG3 0x014c -#define GRF_SOC_VERSION 0x0150 +#define GRF_CSC_MATRIX_COE01_COE00 0x01a0 +#define GRF_CSC_MATRIX_COE10_COE02 0x01a4 +#define GRF_CSC_MATRIX_COE12_COE11 0x01a8 +#define GRF_CSC_MATRIX_COE21_COE20 0x01ac +#define GRF_CSC_MATRIX_COE22 0x01b0 +#define GRF_CSC_MATRIX_OFFSET0 0x01b4 +#define GRF_CSC_MATRIX_OFFSET1 0x01b8 +#define GRF_CSC_MATRIX_OFFSET2 0x01bc +#define GRF_SOC_VERSION 0x0200 #define GRF_MAX_REGISTER GRF_SOC_VERSION enum { @@ -233,46 +262,58 @@ enum { RK628_DEV_GPIO1, RK628_DEV_GPIO2, RK628_DEV_GPIO3, + RK628_DEV_CSI1 = 0x14, RK628_DEV_MAX, }; +enum { + RK628_UNKNOWN, + RK628D_VERSION, + RK628F_VERSION, +}; + +struct mipi_timing { + u8 data_prepare; + u8 data_zero; + u8 data_trail; + u8 clk_prepare; + u8 clk_zero; + u8 clk_trail; + u8 clk_post; +}; + struct rk628 { struct device *dev; struct i2c_client *client; struct regmap *regmap[RK628_DEV_MAX]; + u8 version; void *txphy; + u8 dphy_lane_en; + bool dual_mipi; + struct mipi_timing mipi_timing[2]; + struct mutex rst_lock; + int tx_mode; + struct dentry *debug_dir; }; +int rk628_media_i2c_write(struct rk628 *rk628, u32 reg, u32 val); +int rk628_media_i2c_read(struct rk628 *rk628, u32 reg, u32 *val); +int rk628_media_i2c_update_bits(struct rk628 *rk628, u32 reg, u32 mask, u32 val); + static inline int rk628_i2c_write(struct rk628 *rk628, u32 reg, u32 val) { - int region = (reg >> 16) & 0xff; - int ret = 0; - - ret = regmap_write(rk628->regmap[region], reg, val); - if (ret < 0) - pr_info("%s: i2c err reg=0x%x, val=0x%x, ret=%d\n", __func__, reg, val, ret); - - return ret; + return rk628_media_i2c_write(rk628, reg, val); } static inline int rk628_i2c_read(struct rk628 *rk628, u32 reg, u32 *val) { - int region = (reg >> 16) & 0xff; - int ret = 0; - - ret = regmap_read(rk628->regmap[region], reg, val); - if (ret < 0) - pr_info("%s: i2c err reg=0x%x, val=0x%x ret=%d\n", __func__, reg, *val, ret); - - return ret; + return rk628_media_i2c_read(rk628, reg, val); } static inline int rk628_i2c_update_bits(struct rk628 *rk628, u32 reg, u32 mask, u32 val) { - int region = (reg >> 16) & 0xff; - - return regmap_update_bits(rk628->regmap[region], reg, mask, val); + return rk628_media_i2c_update_bits(rk628, reg, mask, val); } struct rk628 *rk628_i2c_register(struct i2c_client *client); @@ -280,5 +321,7 @@ void rk628_post_process_en(struct rk628 *rk628, struct videomode *src, struct videomode *dst, u64 *dst_pclk); +void rk628_version_parse(struct rk628 *rk628); +void rk628_debugfs_create(struct rk628 *rk628); #endif diff --git a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c index 04fe6908fd3a..d3fa2c3dca8b 100644 --- a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c @@ -44,27 +44,9 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-3)"); -#define DRIVER_VERSION KERNEL_VERSION(0, 0x0, 0x1) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x1, 0x0) #define RK628_BT1120_NAME "rk628-bt1120" -#define EDID_NUM_BLOCKS_MAX 2 -#define EDID_BLOCK_SIZE 128 - -#define RK628_CSI_LINK_FREQ_LOW 350000000 -#define RK628_CSI_LINK_FREQ_HIGH 400000000 -#define RK628_CSI_PIXEL_RATE_LOW 400000000 -#define RK628_CSI_PIXEL_RATE_HIGH 600000000 -#define MIPI_DATARATE_MBPS_LOW 750 -#define MIPI_DATARATE_MBPS_HIGH 1250 - -#define POLL_INTERVAL_MS 1000 -#define MODETCLK_CNT_NUM 1000 -#define MODETCLK_HZ 49500000 -#define RXPHY_CFG_MAX_TIMES 15 -#define CSITX_ERR_RETRY_TIMES 3 - -#define USE_4_LANES 4 -#define YUV422_8BIT 0x1e /* Test Code: 0x44 (HS RX Control of Lane 0) */ #define HSFREQRANGE(x) UPDATE(x, 6, 1) @@ -92,6 +74,7 @@ struct rk628_bt1120 { struct clk *clk_rx_read; struct delayed_work delayed_work_enable_hotplug; struct delayed_work delayed_work_res_change; + struct work_struct work_isr; struct timer_list timer; struct work_struct work_i2c_poll; struct mutex confctl_mutex; @@ -246,14 +229,13 @@ static int rk628_bt1120_s_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings); static int rk628_bt1120_s_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid); -static int rk628_hdmirx_phy_power_on(struct v4l2_subdev *sd); -static int rk628_hdmirx_phy_power_off(struct v4l2_subdev *sd); +static int rk628_hdmirx_inno_phy_power_on(struct v4l2_subdev *sd); +static int rk628_hdmirx_inno_phy_power_off(struct v4l2_subdev *sd); static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd); static void rk628_bt1120_format_change(struct v4l2_subdev *sd); static void enable_stream(struct v4l2_subdev *sd, bool enable); static void rk628_hdmirx_vid_enable(struct v4l2_subdev *sd, bool en); static void rk628_hdmirx_hpd_ctrl(struct v4l2_subdev *sd, bool en); -static void rk628_hdmirx_controller_reset(struct v4l2_subdev *sd); static void rk628_bt1120_initial_setup(struct v4l2_subdev *sd); static inline struct rk628_bt1120 *to_bt1120(struct v4l2_subdev *sd) @@ -333,95 +315,12 @@ static int rk628_bt1120_get_detected_timings(struct v4l2_subdev *sd, { struct rk628_bt1120 *bt1120 = to_bt1120(sd); struct v4l2_bt_timings *bt = &timings->bt; - u32 hact, vact, htotal, vtotal, fps, status; - u32 val; - u32 modetclk_cnt_hs, modetclk_cnt_vs, hs, vs; - u32 hofs_pix, hbp, hfp, vbp, vfp; - u32 tmds_clk, tmdsclk_cnt; - u64 tmp_data; - int retry = 0; + int ret; -__retry: - memset(timings, 0, sizeof(struct v4l2_dv_timings)); - timings->type = V4L2_DV_BT_656_1120; - rk628_i2c_read(bt1120->rk628, HDMI_RX_SCDC_REGS1, &val); - status = val; + ret = rk628_hdmirx_get_timings(bt1120->rk628, timings); + if (ret) + return ret; - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_STS, &val); - bt->interlaced = val & ILACE_STS ? - V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; - - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_HACT_PX, &val); - hact = val & 0xffff; - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_VAL, &val); - vact = val & 0xffff; - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_HT1, &val); - htotal = (val >> 16) & 0xffff; - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_VTL, &val); - vtotal = val & 0xffff; - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_HT1, &val); - hofs_pix = val & 0xffff; - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_VOL, &val); - vbp = (val & 0xffff) + 1; - - rk628_i2c_read(bt1120->rk628, HDMI_RX_HDMI_CKM_RESULT, &val); - tmdsclk_cnt = val & 0xffff; - tmp_data = tmdsclk_cnt; - tmp_data = ((tmp_data * MODETCLK_HZ) + MODETCLK_CNT_NUM / 2); - do_div(tmp_data, MODETCLK_CNT_NUM); - tmds_clk = tmp_data; - if (!htotal || !vtotal) { - v4l2_err(&bt1120->sd, "timing err, htotal:%d, vtotal:%d\n", - htotal, vtotal); - if (retry++ < 5) - goto __retry; - - goto TIMING_ERR; - } - fps = (tmds_clk + (htotal * vtotal) / 2) / (htotal * vtotal); - - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_HT0, &val); - modetclk_cnt_hs = val & 0xffff; - hs = (tmdsclk_cnt * modetclk_cnt_hs + MODETCLK_CNT_NUM / 2) / - MODETCLK_CNT_NUM; - - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_VSC, &val); - modetclk_cnt_vs = val & 0xffff; - vs = (tmdsclk_cnt * modetclk_cnt_vs + MODETCLK_CNT_NUM / 2) / - MODETCLK_CNT_NUM; - vs = (vs + htotal / 2) / htotal; - - if ((hofs_pix < hs) || (htotal < (hact + hofs_pix)) || - (vtotal < (vact + vs + vbp))) { - v4l2_err(sd, "timing err, total:%dx%d, act:%dx%d, hofs:%d, hs:%d, vs:%d, vbp:%d\n", - htotal, vtotal, hact, vact, hofs_pix, hs, vs, vbp); - goto TIMING_ERR; - } - hbp = hofs_pix - hs; - hfp = htotal - hact - hofs_pix; - vfp = vtotal - vact - vs - vbp; - - v4l2_dbg(2, debug, sd, "cnt_num:%d, tmds_cnt:%d, hs_cnt:%d, vs_cnt:%d, hofs:%d\n", - MODETCLK_CNT_NUM, tmdsclk_cnt, modetclk_cnt_hs, modetclk_cnt_vs, hofs_pix); - - bt->width = hact; - bt->height = vact; - bt->hfrontporch = hfp; - bt->hsync = hs; - bt->hbackporch = hbp; - bt->vfrontporch = vfp; - bt->vsync = vs; - bt->vbackporch = vbp; - bt->pixelclock = htotal * vtotal * fps; - - if (bt->interlaced == V4L2_DV_INTERLACED) { - bt->height *= 2; - bt->il_vsync = bt->vsync + 1; - bt->pixelclock /= 2; - } - - v4l2_dbg(1, debug, sd, "SCDC_REGS1:%#x, act:%dx%d, total:%dx%d, fps:%d, pixclk:%llu\n", - status, hact, vact, htotal, vtotal, fps, bt->pixelclock); v4l2_dbg(1, debug, sd, "hfp:%d, hs:%d, hbp:%d, vfp:%d, vs:%d, vbp:%d, interlace:%d\n", bt->hfrontporch, bt->hsync, bt->hbackporch, bt->vfrontporch, bt->vsync, bt->vbackporch, bt->interlaced); @@ -430,10 +329,21 @@ __retry: if (bt1120->scaler_en) *timings = bt1120->timings; - return 0; + return ret; +} -TIMING_ERR: - return -ENOLCK; +static void rk628_hdmirx_plugout(struct v4l2_subdev *sd) +{ + struct rk628_bt1120 *bt1120 = to_bt1120(sd); + + enable_stream(sd, false); + bt1120->nosignal = true; + rk628_bt1120_enable_interrupts(sd, false); + cancel_delayed_work(&bt1120->delayed_work_res_change); + rk628_hdmirx_audio_cancel_work_audio(bt1120->audio_info, true); + rk628_hdmirx_hpd_ctrl(sd, false); + rk628_hdmirx_inno_phy_power_off(sd); + rk628_hdmirx_controller_reset(bt1120->rk628); } static void rk628_hdmirx_config_all(struct v4l2_subdev *sd) @@ -441,12 +351,18 @@ static void rk628_hdmirx_config_all(struct v4l2_subdev *sd) int ret; struct rk628_bt1120 *bt1120 = to_bt1120(sd); - rk628_hdmirx_controller_setup(bt1120->rk628); ret = rk628_hdmirx_phy_setup(sd); - if (ret >= 0) { + if (ret >= 0 && !rk628_hdmirx_scdc_ced_err(bt1120->rk628)) { rk628_bt1120_format_change(sd); bt1120->nosignal = false; + return; } + + if (ret < 0 || rk628_hdmirx_scdc_ced_err(bt1120->rk628)) { + rk628_hdmirx_plugout(sd); + schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, + msecs_to_jiffies(800)); + } } static void rk628_hdmirx_reset_regfile(struct v4l2_subdev *sd) @@ -489,6 +405,7 @@ static void rk628_bt1120_delayed_work_enable_hotplug(struct work_struct *work) bool plugin; mutex_lock(&bt1120->confctl_mutex); + rk628_set_bg_enable(bt1120->rk628, false); bt1120->avi_rcv_rdy = false; plugin = tx_5v_power_present(sd); v4l2_ctrl_s_ctrl(bt1120->detect_tx_5v_ctrl, plugin); @@ -498,20 +415,15 @@ static void rk628_bt1120_delayed_work_enable_hotplug(struct work_struct *work) rk628_bt1120_enable_interrupts(sd, false); rk628_hdmirx_audio_setup(bt1120->audio_info); rk628_hdmirx_set_hdcp(bt1120->rk628, &bt1120->hdcp, bt1120->enable_hdcp); + rk628_hdmirx_controller_setup(bt1120->rk628); rk628_hdmirx_hpd_ctrl(sd, true); rk628_hdmirx_config_all(sd); rk628_bt1120_enable_interrupts(sd, true); rk628_i2c_update_bits(bt1120->rk628, GRF_SYSTEM_CON0, SW_I2S_DATA_OEN_MASK, SW_I2S_DATA_OEN(0)); } else { - rk628_bt1120_enable_interrupts(sd, false); - enable_stream(sd, false); - cancel_delayed_work(&bt1120->delayed_work_res_change); - rk628_hdmirx_audio_cancel_work_audio(bt1120->audio_info, true); - rk628_hdmirx_hpd_ctrl(sd, false); - rk628_hdmirx_phy_power_off(sd); - rk628_hdmirx_controller_reset(sd); bt1120->nosignal = true; + rk628_hdmirx_plugout(sd); rk628_set_io_func_to_gpio(bt1120->rk628); } mutex_unlock(&bt1120->confctl_mutex); @@ -525,6 +437,9 @@ static int rk628_check_resulotion_change(struct v4l2_subdev *sd) u32 old_htotal, old_vtotal; struct v4l2_bt_timings *bt = &bt1120->src_timings.bt; + if (bt1120->rk628->version >= RK628F_VERSION) + return 1; + rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_HT1, &val); htotal = (val >> 16) & 0xffff; rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_VTL, &val); @@ -551,6 +466,8 @@ static void rk628_delayed_work_res_change(struct work_struct *work) bool plugin; mutex_lock(&bt1120->confctl_mutex); + enable_stream(sd, false); + bt1120->nosignal = true; bt1120->avi_rcv_rdy = false; plugin = tx_5v_power_present(sd); v4l2_dbg(1, debug, sd, "%s: 5v_det:%d\n", __func__, plugin); @@ -558,18 +475,28 @@ static void rk628_delayed_work_res_change(struct work_struct *work) if (rk628_check_resulotion_change(sd)) { v4l2_dbg(1, debug, sd, "res change, recfg ctrler and phy!\n"); - rk628_bt1120_enable_interrupts(sd, false); - enable_stream(sd, false); - cancel_delayed_work(&bt1120->delayed_work_res_change); - rk628_hdmirx_audio_cancel_work_audio(bt1120->audio_info, true); - rk628_hdmirx_hpd_ctrl(sd, false); - rk628_hdmirx_phy_power_off(sd); - rk628_hdmirx_controller_reset(sd); - bt1120->nosignal = true; - rk628_hdmirx_reset_regfile(sd); + if (bt1120->rk628->version >= RK628F_VERSION) { + rk628_bt1120_enable_interrupts(sd, false); + rk628_hdmirx_audio_cancel_work_audio(bt1120->audio_info, true); + rk628_hdmirx_hpd_ctrl(sd, false); + rk628_hdmirx_inno_phy_power_off(sd); + rk628_hdmirx_controller_reset(bt1120->rk628); + schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, + msecs_to_jiffies(800)); + } else { + rk628_bt1120_enable_interrupts(sd, false); + enable_stream(sd, false); + cancel_delayed_work(&bt1120->delayed_work_res_change); + rk628_hdmirx_audio_cancel_work_audio(bt1120->audio_info, true); + rk628_hdmirx_controller_setup(bt1120->rk628); + rk628_hdmirx_hpd_ctrl(sd, false); + rk628_hdmirx_inno_phy_power_off(sd); + rk628_hdmirx_controller_reset(bt1120->rk628); + bt1120->nosignal = true; + rk628_hdmirx_reset_regfile(sd); + } } else { rk628_bt1120_format_change(sd); - bt1120->nosignal = false; rk628_bt1120_enable_interrupts(sd, true); } } @@ -661,7 +588,7 @@ static void enable_bt1120tx(struct v4l2_subdev *sd) /* rgb data: cfg SW_R2Y_EN */ rk628_i2c_write(bt1120->rk628, GRF_CSC_CTRL_CON, SW_YUV2VYU_SWP(0) | - SW_R2Y_EN(1)); + SW_R2Y_EN(1) | SW_R2Y_CSC_MODE(2)); } /* if avi packet is not stable, reset ctrl*/ @@ -672,9 +599,19 @@ static void enable_bt1120tx(struct v4l2_subdev *sd) static void enable_stream(struct v4l2_subdev *sd, bool en) { struct rk628_bt1120 *bt1120 = to_bt1120(sd); + u32 val; v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, en ? "en" : "dis"); if (en) { + if (bt1120->rk628->version >= RK628F_VERSION) { + rk628_i2c_read(bt1120->rk628, HDMI_RX_SCDC_REGS2, &val); + if (rk628_hdmirx_scdc_ced_err(bt1120->rk628)) { + rk628_hdmirx_plugout(sd); + schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, + msecs_to_jiffies(800)); + return; + } + } rk628_hdmirx_vid_enable(sd, true); enable_bt1120tx(sd); } else { @@ -724,7 +661,7 @@ static void rk628_post_process_setup(struct v4l2_subdev *sd) dst_bt->pixelclock = dst_pclk; } -static int rk628_hdmirx_phy_power_on(struct v4l2_subdev *sd) +static int rk628_hdmirx_inno_phy_power_on(struct v4l2_subdev *sd) { struct rk628_bt1120 *bt1120 = to_bt1120(sd); int ret, f; @@ -765,10 +702,13 @@ static int rk628_hdmirx_phy_power_on(struct v4l2_subdev *sd) return ret; } -static int rk628_hdmirx_phy_power_off(struct v4l2_subdev *sd) +static int rk628_hdmirx_inno_phy_power_off(struct v4l2_subdev *sd) { struct rk628_bt1120 *bt1120 = to_bt1120(sd); + if (bt1120->rk628->version >= RK628F_VERSION) + return 0; + if (bt1120->rxphy_pwron) { v4l2_dbg(1, debug, sd, "rxphy power off!\n"); rk628_rxphy_power_off(bt1120->rk628); @@ -797,24 +737,14 @@ static void rk628_hdmirx_vid_enable(struct v4l2_subdev *sd, bool en) } } -static void rk628_hdmirx_controller_reset(struct v4l2_subdev *sd) -{ - struct rk628_bt1120 *bt1120 = to_bt1120(sd); - - rk628_control_assert(bt1120->rk628, RGU_HDMIRX_PON); - udelay(10); - rk628_control_deassert(bt1120->rk628, RGU_HDMIRX_PON); - udelay(10); - rk628_i2c_write(bt1120->rk628, HDMI_RX_DMI_SW_RST, 0x000101ff); - rk628_i2c_write(bt1120->rk628, HDMI_RX_DMI_DISABLE_IF, 0x00000000); - rk628_i2c_write(bt1120->rk628, HDMI_RX_DMI_DISABLE_IF, 0x0000017f); - rk628_i2c_write(bt1120->rk628, HDMI_RX_DMI_DISABLE_IF, 0x0001017f); -} - static bool rk628_rcv_supported_res(struct v4l2_subdev *sd, u32 width, u32 height) { u32 i; + struct rk628_bt1120 *bt1120 = to_bt1120(sd); + + if (bt1120->rk628->version >= RK628F_VERSION) + return true; for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { if ((supported_modes[i].width == width) && @@ -836,10 +766,13 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) u32 i, cnt, val; u32 width, height, frame_width, frame_height, status; struct rk628_bt1120 *bt1120 = to_bt1120(sd); - int ret; + int ret = 0; for (i = 0; i < RXPHY_CFG_MAX_TIMES; i++) { - ret = rk628_hdmirx_phy_power_on(sd); + if (bt1120->rk628->version < RK628F_VERSION) + ret = rk628_hdmirx_inno_phy_power_on(sd); + else + rk628_hdmirx_verisyno_phy_power_on(bt1120->rk628); if (ret < 0) { msleep(50); continue; @@ -847,6 +780,7 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) cnt = 0; do { + msleep(20); cnt++; rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_HACT_PX, &val); width = val & 0xffff; @@ -862,7 +796,7 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) __func__, width, height, frame_width, frame_height, status, cnt); rk628_i2c_read(bt1120->rk628, HDMI_RX_PDEC_STS, &val); - if (val & DVI_DET) + if (bt1120->rk628->version < RK628F_VERSION && (val & DVI_DET)) dev_info(bt1120->dev, "DVI mode detected\n"); if (!tx_5v_power_present(sd)) { @@ -872,15 +806,17 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) if (cnt >= 15) break; - } while (((status & 0xfff) != 0xf00) || + } while (((status & 0xfff) < 0xf00) || (!rk628_rcv_supported_res(sd, width, height))); - if (((status & 0xfff) != 0xf00) || + if (((status & 0xfff) < 0xf00) || (!rk628_rcv_supported_res(sd, width, height))) { v4l2_err(sd, "%s hdmi rxphy lock failed, retry:%d\n", __func__, i); continue; } else { + if (bt1120->rk628->version >= RK628F_VERSION) + rk628_hdmirx_phy_prepclk_cfg(bt1120->rk628); break; } } @@ -906,7 +842,10 @@ static void rk628_bt1120_initial_setup(struct v4l2_subdev *sd) /* I2SM0D0 */ rk628_i2c_write(bt1120->rk628, GRF_GPIO0AB_SEL_CON, HIWORD_UPDATE(0x1, 5, 4)); /* hdmirx int en */ - rk628_i2c_write(bt1120->rk628, GRF_INTR0_EN, 0x01000100); + if (bt1120->rk628->version >= RK628F_VERSION) + rk628_i2c_write(bt1120->rk628, GRF_INTR0_EN, 0x02000200); + else + rk628_i2c_write(bt1120->rk628, GRF_INTR0_EN, 0x01000100); udelay(10); rk628_control_assert(bt1120->rk628, RGU_HDMIRX); @@ -930,7 +869,7 @@ static void rk628_bt1120_initial_setup(struct v4l2_subdev *sd) SW_EFUSE_HDCP_EN(0) | SW_HSYNC_POL(1) | SW_VSYNC_POL(1)); - rk628_hdmirx_controller_reset(sd); + rk628_hdmirx_controller_reset(bt1120->rk628); def_edid.pad = 0; def_edid.start_block = 0; @@ -940,7 +879,7 @@ static void rk628_bt1120_initial_setup(struct v4l2_subdev *sd) rk628_hdmirx_set_hdcp(bt1120->rk628, &bt1120->hdcp, false); if (tx_5v_power_present(sd)) - schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, 1000); + schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, 4000); } static void rk628_bt1120_format_change(struct v4l2_subdev *sd) @@ -971,7 +910,7 @@ static void rk628_bt1120_enable_interrupts(struct v4l2_subdev *sd, bool en) u32 pdec_mask = 0, md_mask = 0; struct rk628_bt1120 *bt1120 = to_bt1120(sd); - pdec_mask |= AVI_RCV_ENSET; + pdec_mask = AVI_RCV_ENSET | AVI_CKS_CHG_ICLR; md_mask = VACT_LIN_ENSET | HACT_PIX_ENSET | HS_CLK_ENSET | DE_ACTIVITY_ENSET | VS_ACT_ENSET | HS_ACT_ENSET; v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, en ? "en" : "dis"); @@ -994,42 +933,56 @@ static void rk628_bt1120_enable_interrupts(struct v4l2_subdev *sd, bool en) v4l2_dbg(1, debug, sd, "%s MD_IEN:%#x, PDEC_IEN:%#x\n", __func__, md_ien, pdec_ien); } -static int rk628_bt1120_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +static void rk628_work_isr(struct work_struct *work) { + struct rk628_bt1120 *bt1120 = container_of(work, struct rk628_bt1120, work_isr); + struct v4l2_subdev *sd = &bt1120->sd; u32 md_ints, pdec_ints, fifo_ints, hact, vact; bool plugin; - struct rk628_bt1120 *bt1120 = to_bt1120(sd); void *audio_info = bt1120->audio_info; + bool handled = false; - if (handled == NULL) { - v4l2_err(sd, "handled NULL, err return!\n"); - return -EINVAL; - } + mutex_lock(&bt1120->rk628->rst_lock); + rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_ISTS, &md_ints); rk628_i2c_read(bt1120->rk628, HDMI_RX_PDEC_ISTS, &pdec_ints); - if (rk628_audio_ctsnints_enabled(audio_info)) { - if (pdec_ints & (ACR_N_CHG_ICLR | ACR_CTS_CHG_ICLR)) { - rk628_csi_isr_ctsn(audio_info, pdec_ints); - pdec_ints &= ~(ACR_CTS_CHG_ICLR | ACR_CTS_CHG_ICLR); - *handled = true; - } + if (bt1120->rk628->version >= RK628F_VERSION && + (md_ints & (VACT_LIN_ISTS | HACT_PIX_ISTS | + HS_CLK_ISTS | DE_ACTIVITY_ISTS | + VS_ACT_ISTS | HS_ACT_ISTS) || + pdec_ints & AVI_CKS_CHG_ISTS)) + rk628_set_bg_enable(bt1120->rk628, true); + + plugin = tx_5v_power_present(sd); + if (!plugin) { + rk628_bt1120_enable_interrupts(sd, false); + goto __clear_int; } - if (rk628_audio_fifoints_enabled(audio_info)) { - rk628_i2c_read(bt1120->rk628, HDMI_RX_AUD_FIFO_ISTS, &fifo_ints); - if (fifo_ints & 0x18) { - rk628_csi_isr_fifoints(audio_info, fifo_ints); - *handled = true; + + if (bt1120->rk628->version < RK628F_VERSION) { + if (rk628_audio_ctsnints_enabled(audio_info)) { + if (pdec_ints & (ACR_N_CHG_ICLR | ACR_CTS_CHG_ICLR)) { + rk628_csi_isr_ctsn(audio_info, pdec_ints); + pdec_ints &= ~(ACR_CTS_CHG_ICLR | ACR_CTS_CHG_ICLR); + handled = true; + } + } + if (rk628_audio_fifoints_enabled(audio_info)) { + rk628_i2c_read(bt1120->rk628, HDMI_RX_AUD_FIFO_ISTS, &fifo_ints); + if (fifo_ints & 0x18) { + rk628_csi_isr_fifoints(audio_info, fifo_ints); + handled = true; + } } } if (bt1120->vid_ints_en) { rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_ISTS, &md_ints); - plugin = tx_5v_power_present(sd); v4l2_dbg(1, debug, sd, "%s: md_ints: %#x, pdec_ints:%#x, plugin: %d\n", __func__, md_ints, pdec_ints, plugin); if ((md_ints & (VACT_LIN_ISTS | HACT_PIX_ISTS | HS_CLK_ISTS | DE_ACTIVITY_ISTS | - VS_ACT_ISTS | HS_ACT_ISTS)) - && plugin) { + VS_ACT_ISTS | HS_ACT_ISTS) || + pdec_ints & AVI_CKS_CHG_ISTS)) { rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_HACT_PX, &hact); rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_VAL, &vact); @@ -1037,13 +990,15 @@ static int rk628_bt1120_isr(struct v4l2_subdev *sd, u32 status, bool *handled) __func__, hact, vact); rk628_bt1120_enable_interrupts(sd, false); - enable_stream(sd, false); - bt1120->nosignal = true; + if (bt1120->rk628->version < RK628F_VERSION) { + enable_stream(sd, false); + bt1120->nosignal = true; + } schedule_delayed_work(&bt1120->delayed_work_res_change, HZ / 2); v4l2_dbg(1, debug, sd, "%s: hact/vact change, md_ints: %#x\n", __func__, (u32)(md_ints & (VACT_LIN_ISTS | HACT_PIX_ISTS))); - *handled = true; + handled = true; } if ((pdec_ints & AVI_RCV_ISTS) && plugin && !bt1120->avi_rcv_rdy) { @@ -1052,16 +1007,34 @@ static int rk628_bt1120_isr(struct v4l2_subdev *sd, u32 status, bool *handled) /* After get the AVI_RCV interrupt state, disable interrupt. */ rk628_i2c_write(bt1120->rk628, HDMI_RX_PDEC_IEN_CLR, AVI_RCV_ISTS); - *handled = true; + handled = true; } } - if (*handled != true) + if (!handled) v4l2_dbg(1, debug, sd, "%s: unhandled interrupt!\n", __func__); +__clear_int: /* clear interrupts */ rk628_i2c_write(bt1120->rk628, HDMI_RX_MD_ICLR, 0xffffffff); rk628_i2c_write(bt1120->rk628, HDMI_RX_PDEC_ICLR, 0xffffffff); - rk628_i2c_write(bt1120->rk628, GRF_INTR0_CLR_EN, 0x01000100); + if (bt1120->rk628->version >= RK628F_VERSION) + rk628_i2c_write(bt1120->rk628, GRF_INTR0_CLR_EN, 0x02000200); + else + rk628_i2c_write(bt1120->rk628, GRF_INTR0_CLR_EN, 0x01000100); + + mutex_unlock(&bt1120->rk628->rst_lock); +} + +static int rk628_bt1120_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + struct rk628_bt1120 *bt1120 = to_bt1120(sd); + + if (handled == NULL) { + v4l2_err(sd, "handled NULL, err return!\n"); + return -EINVAL; + } + + schedule_work(&bt1120->work_isr); return 0; } @@ -1069,7 +1042,7 @@ static int rk628_bt1120_isr(struct v4l2_subdev *sd, u32 status, bool *handled) static irqreturn_t rk628_bt1120_irq_handler(int irq, void *dev_id) { struct rk628_bt1120 *bt1120 = dev_id; - bool handled = false; + bool handled = true; rk628_bt1120_isr(&bt1120->sd, 0, &handled); @@ -1216,10 +1189,22 @@ static int rk628_bt1120_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *cfg) { + struct rk628_bt1120 *bt1120 = to_bt1120(sd); + cfg->type = V4L2_MBUS_BT656; +#if KERNEL_VERSION(5, 18, 0) <= LINUX_VERSION_CODE cfg->bus.parallel.flags = V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_PCLK_SAMPLE_RISING; + if (bt1120->dual_edge) + cfg->bus.parallel.flags |= V4L2_MBUS_PCLK_SAMPLE_FALLING; +#else + cfg->flags = V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_PCLK_SAMPLE_RISING; + if (bt1120->dual_edge) + cfg->flags |= V4L2_MBUS_PCLK_SAMPLE_FALLING; +#endif return 0; } @@ -1483,7 +1468,7 @@ static int rk628_bt1120_s_edid(struct v4l2_subdev *sd, bt1120->edid_blocks_written = edid->blocks; udelay(100); - if (tx_5v_power_present(sd)) + if (tx_5v_power_present(sd) && bt1120->rk628->version < RK628F_VERSION) rk628_hdmirx_hpd_ctrl(sd, true); return 0; @@ -1857,6 +1842,8 @@ static int rk628_bt1120_probe(struct i2c_client *client, rk628_cru_initialize(rk628); + rk628_version_parse(rk628); + #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API v4l2_i2c_subdev_init(sd, client, &rk628_bt1120_ops); sd->internal_ops = &bt1120_subdev_internal_ops; @@ -1931,6 +1918,7 @@ static int rk628_bt1120_probe(struct i2c_client *client, rk628_bt1120_delayed_work_enable_hotplug); INIT_DELAYED_WORK(&bt1120->delayed_work_res_change, rk628_delayed_work_res_change); + INIT_WORK(&bt1120->work_isr, rk628_work_isr); bt1120->audio_info = rk628_hdmirx_audioinfo_alloc(dev, &bt1120->confctl_mutex, rk628, @@ -1995,6 +1983,7 @@ err_work_queues: flush_work(&bt1120->work_i2c_poll); cancel_delayed_work(&bt1120->delayed_work_enable_hotplug); cancel_delayed_work(&bt1120->delayed_work_res_change); + cancel_work_sync(&bt1120->work_isr); rk628_hdmirx_audio_destroy(bt1120->audio_info); err_hdl: mutex_destroy(&bt1120->confctl_mutex); @@ -2013,6 +2002,7 @@ static void rk628_bt1120_remove(struct i2c_client *client) } cancel_delayed_work_sync(&bt1120->delayed_work_enable_hotplug); cancel_delayed_work_sync(&bt1120->delayed_work_res_change); + cancel_work_sync(&bt1120->work_isr); rk628_hdmirx_audio_cancel_work_audio(bt1120->audio_info, true); rk628_hdmirx_audio_cancel_work_rate_change(bt1120->audio_info, true); diff --git a/drivers/media/i2c/rk628/rk628_combtxphy.c b/drivers/media/i2c/rk628/rk628_combtxphy.c index ad40a58eb5d4..ec42f539857c 100644 --- a/drivers/media/i2c/rk628/rk628_combtxphy.c +++ b/drivers/media/i2c/rk628/rk628_combtxphy.c @@ -49,8 +49,11 @@ static void rk628_combtxphy_dsi_power_on(struct rk628 *rk628) rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_PD_PLL, 0); usleep_range(100, 200); rk628_i2c_update_bits(rk628, COMBTXPHY_CON9, SW_DSI_FSET_EN_MASK | - SW_DSI_RCAL_EN_MASK, SW_DSI_FSET_EN | - SW_DSI_RCAL_EN); + SW_DSI_RCAL_EN_MASK | SW_LPTX_SR_TRIM_MASK, SW_DSI_FSET_EN | + SW_DSI_RCAL_EN | SW_LPTX_SR_TRIM(7)); + if (rk628->tx_mode && rk628->dual_mipi) + rk628_i2c_update_bits(rk628, COMBTXPHY_CON6, SW_PLL_CTRL0_MASK, SW_PLL_CTRL0(1)); + usleep_range(100, 200); } @@ -111,7 +114,7 @@ void rk628_txphy_set_mode(struct rk628 *rk628, enum phy_mode mode) unsigned int flags = bus_width & 0xff; fhsc = fin * (fhsc / fin); - if (fhsc < 80 || fhsc > 1500) + if (fhsc < 80 || fhsc > 1800) return; else if (fhsc < 375) txphy->rate_div = 4; diff --git a/drivers/media/i2c/rk628/rk628_combtxphy.h b/drivers/media/i2c/rk628/rk628_combtxphy.h index 406dc32f956d..8842537a7f14 100644 --- a/drivers/media/i2c/rk628/rk628_combtxphy.h +++ b/drivers/media/i2c/rk628/rk628_combtxphy.h @@ -41,6 +41,8 @@ #define SW_PLL_FB_DIV(x) UPDATE(x, 14, 10) #define SW_PLL_FRAC_DIV(x) UPDATE(x, 9, 0) #define COMBTXPHY_CON6 COMBTXPHY_REG(0x0018) +#define SW_PLL_CTRL0_MASK GENMASK(2, 0) +#define SW_PLL_CTRL0(x) UPDATE(x, 2, 0) #define COMBTXPHY_CON7 COMBTXPHY_REG(0x001c) #define SW_TX_RTERM_MASK GENMASK(22, 20) #define SW_TX_RTERM(x) UPDATE(x, 22, 20) @@ -52,6 +54,14 @@ #define SW_TX_CTL_CON4(x) UPDATE(x, 9, 8) #define COMBTXPHY_CON8 COMBTXPHY_REG(0x0020) #define COMBTXPHY_CON9 COMBTXPHY_REG(0x0024) +#define SW_HSTX_AMP_TRIM_MASK GENMASK(2, 0) +#define SW_HSTX_AMP_TRIM(x) UPDATE(x, 2, 0) +#define SW_LPTX_SR_TRIM_MASK GENMASK(6, 4) +#define SW_LPTX_SR_TRIM(x) UPDATE(x, 6, 4) +#define SW_DSI_RCAL_CTRL_MASK GENMASK(23, 16) +#define SW_DSI_RCAL_CTRL(x) UPDATE(x, 23, 16) +#define SW_DSI_RCAL_TRIM_MASK GENMASK(27, 24) +#define SW_DSI_RCAL_TRIM(x) UPDATE(x, 27, 24) #define SW_DSI_FSET_EN_MASK BIT(29) #define SW_DSI_FSET_EN BIT(29) #define SW_DSI_RCAL_EN_MASK BIT(28) diff --git a/drivers/media/i2c/rk628/rk628_cru.c b/drivers/media/i2c/rk628/rk628_cru.c index 8fd6924176ee..2005b280e339 100644 --- a/drivers/media/i2c/rk628/rk628_cru.c +++ b/drivers/media/i2c/rk628/rk628_cru.c @@ -66,6 +66,9 @@ static unsigned long rk628_cru_clk_get_rate_pll(struct rk628 *rk628, u64 foutvco, foutpostdiv; u32 offset, val; + if (id == CGU_CLK_APLL && rk628->version == RK628D_VERSION) + return 0; + rk628_i2c_read(rk628, CRU_MODE_CON00, &val); if (id == CGU_CLK_CPLL) { val &= CLK_CPLL_MODE_MASK; @@ -74,15 +77,22 @@ static unsigned long rk628_cru_clk_get_rate_pll(struct rk628 *rk628, return parent_rate; offset = 0x00; - } else { + } else if (id == CGU_CLK_GPLL) { val &= CLK_GPLL_MODE_MASK; val >>= CLK_GPLL_MODE_SHIFT; if (val == CLK_GPLL_MODE_OSC) return parent_rate; offset = 0x20; + } else { + val &= CLK_APLL_MODE_MASK; + val >>= CLK_APLL_MODE_SHIFT; + if (val == CLK_APLL_MODE_OSC) + return parent_rate; + offset = 0x40; } + rk628_i2c_read(rk628, offset + CRU_CPLL_CON0, &con0); rk628_i2c_read(rk628, offset + CRU_CPLL_CON1, &con1); rk628_i2c_read(rk628, offset + CRU_CPLL_CON2, &con2); @@ -127,6 +137,9 @@ static unsigned long rk628_cru_clk_set_rate_pll(struct rk628 *rk628, u64 foutvco, foutpostdiv; u32 offset, val; + if (id == CGU_CLK_APLL && rk628->version == RK628D_VERSION) + return 0; + /* * FREF : 10MHz ~ 800MHz * FREFDIV : 1MHz ~ 40MHz @@ -141,8 +154,10 @@ static unsigned long rk628_cru_clk_set_rate_pll(struct rk628 *rk628, if (id == CGU_CLK_CPLL) offset = 0x00; - else + else if (id == CGU_CLK_GPLL) offset = 0x20; + else + offset = 0x40; if (fin == fout) { rk628_i2c_write(rk628, offset + CRU_CPLL_CON0, PLL_BYPASS(1)); @@ -282,17 +297,24 @@ static unsigned long rk628_cru_clk_set_rate_sclk_vop(struct rk628 *rk628, } static unsigned long rk628_cru_clk_set_rate_sclk_hdmirx_aud(struct rk628 *rk628, - unsigned long rate) + unsigned long rate) { u64 parent_rate; u8 div; - parent_rate = rk628_cru_clk_set_rate_pll(rk628, CGU_CLK_GPLL, rate*4); + if (rk628->version >= RK628F_VERSION) + parent_rate = rk628_cru_clk_set_rate_pll(rk628, CGU_CLK_APLL, rate*4); + else + parent_rate = rk628_cru_clk_set_rate_pll(rk628, CGU_CLK_GPLL, rate*4); div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate); do_div(parent_rate, div); rate = parent_rate; - rk628_i2c_write(rk628, CRU_CLKSEL_CON05, 0x3fc0 << 16 | ((div - 1) << 6) | - CLK_HDMIRX_AUD_SEL << 16 | CLK_HDMIRX_AUD_SEL); + if (rk628->version >= RK628F_VERSION) + rk628_i2c_write(rk628, CRU_CLKSEL_CON05, CLK_HDMIRX_AUD_DIV(div - 1) | + CLK_HDMIRX_AUD_SEL_V2(2)); + else + rk628_i2c_write(rk628, CRU_CLKSEL_CON05, CLK_HDMIRX_AUD_DIV(div - 1) | + CLK_HDMIRX_AUD_SEL_V1(1)); return rate; } @@ -304,11 +326,17 @@ static unsigned long rk628_cru_clk_get_rate_sclk_hdmirx_aud(struct rk628 *rk628) u32 val; rk628_i2c_read(rk628, CRU_CLKSEL_CON05, &val); - if (val & CLK_HDMIRX_AUD_SEL) - parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); + div = ((val & CLK_HDMIRX_AUD_DIV_MASK) >> 6) + 1; + if (rk628->version >= RK628F_VERSION) + val = (val & CLK_HDMIRX_AUD_SEL_MASK_V2) >> 14; else + val = (val & CLK_HDMIRX_AUD_SEL_MASK_V1) >> 15; + if (!val) parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL); - div = ((val&0x3fc0) >> 6) + 1; + else if (val == 2) + parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_APLL); + else + parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); do_div(parent_rate, div); rate = parent_rate; return rate; @@ -424,7 +452,11 @@ EXPORT_SYMBOL(rk628_clk_mux_testout); int rk628_clk_set_rate(struct rk628 *rk628, unsigned int id, unsigned long rate) { + if (id == CGU_CLK_APLL && rk628->version == RK628D_VERSION) + return -EINVAL; + switch (id) { + case CGU_CLK_APLL: case CGU_CLK_CPLL: case CGU_CLK_GPLL: rk628_cru_clk_set_rate_pll(rk628, id, rate); @@ -442,7 +474,7 @@ int rk628_clk_set_rate(struct rk628 *rk628, unsigned int id, rk628_cru_clk_set_rate_sclk_hdmirx_aud(rk628, rate); break; default: - return -1; + return -EINVAL; } return 0; @@ -453,7 +485,11 @@ unsigned long rk628_clk_get_rate(struct rk628 *rk628, unsigned int id) { unsigned long rate; + if (id == CGU_CLK_APLL && rk628->version == RK628D_VERSION) + return 0; + switch (id) { + case CGU_CLK_APLL: case CGU_CLK_CPLL: case CGU_CLK_GPLL: rate = rk628_cru_clk_get_rate_pll(rk628, id); @@ -519,6 +555,7 @@ static const struct rk628_rgu_data rk628_rgu_data[] = { RSTGEN(RGU_HDMIRX_PON, CRU_SOFTRST_CON02, 12), RSTGEN(RGU_TXBYTEHS, CRU_SOFTRST_CON02, 13), RSTGEN(RGU_TXESC, CRU_SOFTRST_CON02, 14), + RSTGEN(RGU_CSI1, CRU_SOFTRST_CON02, 15), }; static int rk628_rgu_update(struct rk628 *rk628, unsigned long id, int assert) diff --git a/drivers/media/i2c/rk628/rk628_cru.h b/drivers/media/i2c/rk628/rk628_cru.h index 6970e894ce06..48f6abe7299a 100644 --- a/drivers/media/i2c/rk628/rk628_cru.h +++ b/drivers/media/i2c/rk628/rk628_cru.h @@ -46,7 +46,16 @@ #define CRU_GPLL_CON2 CRU_REG(0x0028) #define CRU_GPLL_CON3 CRU_REG(0x002c) #define CRU_GPLL_CON4 CRU_REG(0x0030) +#define CRU_APLL_CON0 CRU_REG(0x0040) +#define CRU_APLL_CON1 CRU_REG(0x0044) +#define CRU_APLL_CON2 CRU_REG(0x0048) +#define CRU_APLL_CON3 CRU_REG(0x004c) +#define CRU_APLL_CON4 CRU_REG(0x0050) #define CRU_MODE_CON00 CRU_REG(0x0060) +#define CLK_APLL_MODE_MASK BIT(4) +#define CLK_APLL_MODE_SHIFT 4 +#define CLK_APLL_MODE_APLL 1 +#define CLK_APLL_MODE_OSC 0 #define CLK_GPLL_MODE_MASK BIT(2) #define CLK_GPLL_MODE_SHIFT 2 #define CLK_GPLL_MODE_GPLL 1 @@ -69,7 +78,12 @@ #define CRU_CLKSEL_CON03 CRU_REG(0x008c) #define CRU_CLKSEL_CON04 CRU_REG(0x0090) #define CRU_CLKSEL_CON05 CRU_REG(0x0094) -#define CLK_HDMIRX_AUD_SEL BIT(15) +#define CLK_HDMIRX_AUD_DIV_MASK GENMASK(13, 6) +#define CLK_HDMIRX_AUD_DIV(x) HIWORD_UPDATE(x, 13, 6) +#define CLK_HDMIRX_AUD_SEL_V1(x) HIWORD_UPDATE(x, 15, 15) +#define CLK_HDMIRX_AUD_SEL_MASK_V1 GENMASK(15, 15) +#define CLK_HDMIRX_AUD_SEL_V2(x) HIWORD_UPDATE(x, 15, 14) +#define CLK_HDMIRX_AUD_SEL_MASK_V2 GENMASK(15, 14) #define CRU_CLKSEL_CON06 CRU_REG(0x0098) #define SCLK_UART_SEL(x) HIWORD_UPDATE(x, 15, 14) #define SCLK_UART_SEL_MASK GENMASK(15, 14) @@ -146,6 +160,7 @@ #define CGU_I2S_MCLKOUT 36 #define CGU_BT1120DEC 37 #define CGU_SCLK_UART 38 +#define CGU_CLK_APLL 39 #define RGU_LOGIC 0 #define RGU_CRU 1 @@ -178,6 +193,7 @@ #define RGU_HDMIRX_PON 28 #define RGU_TXBYTEHS 29 #define RGU_TXESC 30 +#define RGU_CSI1 31 unsigned long rk628_clk_get_rate(struct rk628 *rk628, unsigned int id); int rk628_clk_set_rate(struct rk628 *rk628, unsigned int id, diff --git a/drivers/media/i2c/rk628/rk628_csi.h b/drivers/media/i2c/rk628/rk628_csi.h index 183b8769e37a..d8327a577e1e 100644 --- a/drivers/media/i2c/rk628/rk628_csi.h +++ b/drivers/media/i2c/rk628/rk628_csi.h @@ -19,6 +19,8 @@ #define VOP_YU_SWAP(x) UPDATE(x, 14, 14) #define VOP_UV_SWAP_MASK BIT(13) #define VOP_UV_SWAP(x) UPDATE(x, 13, 13) +#define VOP_YUV422_MODE_MASK GENMASK(11, 10) +#define VOP_YUV422_MODE(x) UPDATE(x, 11, 10) #define VOP_YUV422_EN_MASK BIT(12) #define VOP_YUV422_EN(x) UPDATE(x, 12, 12) #define VOP_P2_EN_MASK BIT(8) @@ -58,6 +60,11 @@ #define VOP_DT_USERDEFINE_EN(x) UPDATE(x, 1, 1) #define VOP_PATH_EN_MASK BIT(0) #define VOP_PATH_EN(x) UPDATE(x, 0, 0) +#define CSITX_VOP_FILTER_CTRL (CSITX_BASE + 0x0044) +#define VOP_FILTER_EN_MASK BIT(0) +#define VOP_FILTER_EN(x) UPDATE(x, 0, 0) +#define VOP_FILTER_MASK GENMASK(15, 8) +#define VOP_FILTER(x) UPDATE(x, 15, 8) #define CSITX_VOP_PATH_PKT_CTRL (CSITX_BASE + 0x0050) #define CSITX_CSITX_STATUS0 (CSITX_BASE + 0x0070) #define CSITX_CSITX_STATUS1 (CSITX_BASE + 0x0074) @@ -77,5 +84,27 @@ #define CSI_DPHY_EN(x) UPDATE(x, 7, 3) #define DPHY_ENABLECLK BIT(3) #define CSI_MAX_REGISTER CSITX_DPHY_CTRL +//for rk628f csi1 +#define CSITX1_BASE 0x00140000 +#define CSITX1_CONFIG_DONE (CSITX1_BASE + 0x0000) +#define CSITX1_CSITX_EN (CSITX1_BASE + 0x0004) +#define CSITX1_CSITX_VERSION (CSITX1_BASE + 0x0008) +#define CSITX1_SYS_CTRL0_IMD (CSITX1_BASE + 0x0010) +#define CSITX1_SYS_CTRL1 (CSITX1_BASE + 0x0014) +#define CSITX1_SYS_CTRL2 (CSITX1_BASE + 0x0018) +#define CSITX1_SYS_CTRL3_IMD (CSITX1_BASE + 0x001c) +#define CSITX1_TIMING_HPW_PADDING_NUM (CSITX1_BASE + 0x0030) +#define CSITX1_VOP_PATH_CTRL (CSITX1_BASE + 0x0040) +#define CSITX1_VOP_FILTER_CTRL (CSITX1_BASE + 0x0044) +#define CSITX1_VOP_PATH_PKT_CTRL (CSITX1_BASE + 0x0050) +#define CSITX1_CSITX_STATUS0 (CSITX1_BASE + 0x0070) +#define CSITX1_CSITX_STATUS1 (CSITX1_BASE + 0x0074) +#define CSITX1_ERR_INTR_EN_IMD (CSITX1_BASE + 0x0090) +#define CSITX1_ERR_INTR_CLR_IMD (CSITX1_BASE + 0x0094) +#define CSITX1_ERR_INTR_STATUS_IMD (CSITX1_BASE + 0x0098) +#define CSITX1_ERR_INTR_RAW_STATUS_IMD (CSITX1_BASE + 0x009c) +#define CSITX1_LPDT_DATA_IMD (CSITX1_BASE + 0x00a8) +#define CSITX1_DPHY_CTRL (CSITX1_BASE + 0x00b0) +#define CSI1_MAX_REGISTER CSITX1_DPHY_CTRL #endif diff --git a/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/drivers/media/i2c/rk628/rk628_csi_v4l2.c index 5253d3533a27..1dfb1ddf557b 100644 --- a/drivers/media/i2c/rk628/rk628_csi_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_csi_v4l2.c @@ -43,32 +43,15 @@ #include "rk628_dsi.h" #include "rk628_hdmirx.h" #include "rk628_mipi_dphy.h" +#include "rk628_post_process.h" static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-3)"); -#define DRIVER_VERSION KERNEL_VERSION(0, 0x0, 0x8) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x1, 0x0) #define RK628_CSI_NAME "rk628-csi" -#define EDID_NUM_BLOCKS_MAX 2 -#define EDID_BLOCK_SIZE 128 - -#define RK628_CSI_LINK_FREQ_LOW 350000000 -#define RK628_CSI_LINK_FREQ_HIGH 600000000 -#define RK628_CSI_PIXEL_RATE_LOW 400000000 -#define RK628_CSI_PIXEL_RATE_HIGH 600000000 -#define MIPI_DATARATE_MBPS_LOW 700 -#define MIPI_DATARATE_MBPS_HIGH 1250 - -#define POLL_INTERVAL_MS 1000 -#define MODETCLK_CNT_NUM 1000 -#define MODETCLK_HZ 49500000 -#define RXPHY_CFG_MAX_TIMES 15 -#define CSITX_ERR_RETRY_TIMES 3 - -#define YUV422_8BIT 0x1e - enum tx_mode_type { CSI_MODE, DSI_MODE, @@ -103,9 +86,11 @@ struct rk628_csi { struct clk *clk_rx_read; struct delayed_work delayed_work_enable_hotplug; struct delayed_work delayed_work_res_change; + struct work_struct work_isr; struct timer_list timer; struct work_struct work_i2c_poll; struct mutex confctl_mutex; + struct rkmodule_multi_dev_info multi_dev_info; const struct rk628_csi_mode *cur_mode; const char *module_facing; const char *module_name; @@ -149,6 +134,7 @@ struct rk628_csi_mode { static const s64 link_freq_menu_items[] = { RK628_CSI_LINK_FREQ_LOW, RK628_CSI_LINK_FREQ_HIGH, + RK628_CSI_LINK_FREQ_925M, }; static const struct v4l2_dv_timings_cap rk628_csi_timings_cap = { @@ -199,6 +185,59 @@ static u8 edid_init_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC1, }; +static u8 rk628f_edid_init_data[] = { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x24, 0xD0, 0x8F, 0x62, 0x01, 0x00, 0x00, 0x00, + 0x2D, 0x21, 0x01, 0x03, 0x80, 0x78, 0x44, 0x78, + 0x0A, 0xCF, 0x74, 0xA3, 0x57, 0x4C, 0xB0, 0x23, + 0x09, 0x48, 0x4C, 0x21, 0x08, 0x00, 0x61, 0x40, + 0x01, 0x01, 0x81, 0x00, 0x95, 0x00, 0xA9, 0xC0, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0xE8, + 0x00, 0x30, 0xF2, 0x70, 0x5A, 0x80, 0xB0, 0x58, + 0x8A, 0x00, 0xC4, 0x8E, 0x21, 0x00, 0x00, 0x1E, + 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, + 0x58, 0x2C, 0x45, 0x00, 0xB9, 0xA8, 0x42, 0x00, + 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x49, + 0x46, 0x50, 0x20, 0x44, 0x69, 0x73, 0x70, 0x6C, + 0x61, 0x79, 0x0A, 0x20, 0x00, 0x00, 0x00, 0xFD, + 0x00, 0x3B, 0x46, 0x1F, 0x8C, 0x3C, 0x00, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xA8, + + 0x02, 0x03, 0x39, 0xF2, 0x4D, 0x01, 0x03, 0x12, + 0x13, 0x84, 0x22, 0x1F, 0x90, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, + 0x00, 0x00, 0x6D, 0x03, 0x0C, 0x00, 0x10, 0x00, + 0x00, 0x44, 0x20, 0x00, 0x60, 0x03, 0x02, 0x01, + 0x67, 0xD8, 0x5D, 0xC4, 0x01, 0x78, 0xC0, 0x00, + 0xE3, 0x05, 0x03, 0x01, 0xE4, 0x0F, 0x00, 0x18, + 0x00, 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, + 0x40, 0x58, 0x2C, 0x45, 0x00, 0xB9, 0xA8, 0x42, + 0x00, 0x00, 0x1E, 0x08, 0xE8, 0x00, 0x30, 0xF2, + 0x70, 0x5A, 0x80, 0xB0, 0x58, 0x8A, 0x00, 0xC4, + 0x8E, 0x21, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, +}; + +static const struct mipi_timing rk628d_csi_mipi = { + 0x4a, 0xf, 0x5d, 0x3a, 0x3a, 0x5a, 0x1f +}; + +static const struct mipi_timing rk628f_csi0_mipi = { + 0x4a, 0xf, 0x5d, 0x3a, 0x3a, 0x5a, 0x1f +}; + +static const struct mipi_timing rk628f_csi1_mipi = { +//data-pre, data-zero, data-trail, clk-pre, clk-zero, clk-trail, clk-post + 0x4a, 0xf, 0x66, 0x3a, 0x3a, 0x5a, 0x1f +}; + +static const struct mipi_timing rk628f_dsi0_mipi = { + 0x70, 0x1c, 0x7f, 0x70, 0x3f, 0x7f, 0x1f +}; + static struct rkmodule_csi_dphy_param rk3588_dcphy_param = { .vendor = PHY_VENDOR_SAMSUNG, .lp_vol_ref = 0, @@ -212,6 +251,15 @@ static struct rkmodule_csi_dphy_param rk3588_dcphy_param = { static const struct rk628_csi_mode supported_modes[] = { { + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 600000, + }, + .hts_def = 4400, + .vts_def = 2250, + }, { .width = 3840, .height = 2160, .max_fps = { @@ -284,19 +332,19 @@ static int rk628_csi_s_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid); static int mipi_dphy_power_on(struct rk628_csi *csi); static void mipi_dphy_power_off(struct rk628_csi *csi); -static int rk628_hdmirx_phy_power_on(struct v4l2_subdev *sd); -static int rk628_hdmirx_phy_power_off(struct v4l2_subdev *sd); +static int rk628_hdmirx_inno_phy_power_on(struct v4l2_subdev *sd); +static int rk628_hdmirx_inno_phy_power_off(struct v4l2_subdev *sd); static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd); static void rk628_csi_format_change(struct v4l2_subdev *sd); static void enable_stream(struct v4l2_subdev *sd, bool enable); static void rk628_hdmirx_vid_enable(struct v4l2_subdev *sd, bool en); static void rk628_csi_set_csi(struct v4l2_subdev *sd); static void rk628_hdmirx_hpd_ctrl(struct v4l2_subdev *sd, bool en); -static void rk628_hdmirx_controller_reset(struct v4l2_subdev *sd); static bool rk628_rcv_supported_res(struct v4l2_subdev *sd, u32 width, u32 height); static void rk628_dsi_set_scs(struct rk628_csi *csi); static void rk628_dsi_enable(struct v4l2_subdev *sd); +static void rk628_csi_disable_stream(struct v4l2_subdev *sd); static inline struct rk628_csi *to_csi(struct v4l2_subdev *sd) { @@ -376,98 +424,21 @@ static int rk628_csi_get_detected_timings(struct v4l2_subdev *sd, { struct rk628_csi *csi = to_csi(sd); struct v4l2_bt_timings *bt = &timings->bt; - u32 hact, vact, htotal, vtotal, fps, status; - u32 val; - u32 modetclk_cnt_hs, modetclk_cnt_vs, hs, vs; - u32 hofs_pix, hbp, hfp, vbp, vfp; - u32 tmds_clk, tmdsclk_cnt; - u64 tmp_data; - int retry = 0; + int ret; -__retry: - memset(timings, 0, sizeof(struct v4l2_dv_timings)); - timings->type = V4L2_DV_BT_656_1120; - rk628_i2c_read(csi->rk628, HDMI_RX_SCDC_REGS1, &val); - status = val; + ret = rk628_hdmirx_get_timings(csi->rk628, timings); + if (ret) + return ret; - rk628_i2c_read(csi->rk628, HDMI_RX_MD_STS, &val); - bt->interlaced = val & ILACE_STS ? - V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; - - rk628_i2c_read(csi->rk628, HDMI_RX_MD_HACT_PX, &val); - hact = val & 0xffff; - rk628_i2c_read(csi->rk628, HDMI_RX_MD_VAL, &val); - vact = val & 0xffff; - rk628_i2c_read(csi->rk628, HDMI_RX_MD_HT1, &val); - htotal = (val >> 16) & 0xffff; - rk628_i2c_read(csi->rk628, HDMI_RX_MD_VTL, &val); - vtotal = val & 0xffff; - rk628_i2c_read(csi->rk628, HDMI_RX_MD_HT1, &val); - hofs_pix = val & 0xffff; - rk628_i2c_read(csi->rk628, HDMI_RX_MD_VOL, &val); - vbp = (val & 0xffff) + 1; - - rk628_i2c_read(csi->rk628, HDMI_RX_HDMI_CKM_RESULT, &val); - tmdsclk_cnt = val & 0xffff; - tmp_data = tmdsclk_cnt; - tmp_data = ((tmp_data * MODETCLK_HZ) + MODETCLK_CNT_NUM / 2); - do_div(tmp_data, MODETCLK_CNT_NUM); - tmds_clk = tmp_data; - if (!htotal || !vtotal) { - v4l2_err(&csi->sd, "timing err, htotal:%d, vtotal:%d\n", - htotal, vtotal); - if (retry++ < 5) - goto __retry; - - goto TIMING_ERR; - } - fps = (tmds_clk + (htotal * vtotal) / 2) / (htotal * vtotal); - - rk628_i2c_read(csi->rk628, HDMI_RX_MD_HT0, &val); - modetclk_cnt_hs = val & 0xffff; - hs = (tmdsclk_cnt * modetclk_cnt_hs + MODETCLK_CNT_NUM / 2) / - MODETCLK_CNT_NUM; - - rk628_i2c_read(csi->rk628, HDMI_RX_MD_VSC, &val); - modetclk_cnt_vs = val & 0xffff; - vs = (tmdsclk_cnt * modetclk_cnt_vs + MODETCLK_CNT_NUM / 2) / - MODETCLK_CNT_NUM; - vs = (vs + htotal / 2) / htotal; - - if ((hofs_pix < hs) || (htotal < (hact + hofs_pix)) || - (vtotal < (vact + vs + vbp))) { - v4l2_err(sd, "timing err, total:%dx%d, act:%dx%d, hofs:%d, hs:%d, vs:%d, vbp:%d\n", - htotal, vtotal, hact, vact, hofs_pix, hs, vs, vbp); - goto TIMING_ERR; - } - hbp = hofs_pix - hs; - hfp = htotal - hact - hofs_pix; - vfp = vtotal - vact - vs - vbp; - - v4l2_dbg(2, debug, sd, "cnt_num:%d, tmds_cnt:%d, hs_cnt:%d, vs_cnt:%d, hofs:%d\n", - MODETCLK_CNT_NUM, tmdsclk_cnt, modetclk_cnt_hs, modetclk_cnt_vs, hofs_pix); - - bt->width = hact; - bt->height = vact; - bt->hfrontporch = hfp; - bt->hsync = hs; - bt->hbackporch = hbp; - bt->vfrontporch = vfp; - bt->vsync = vs; - bt->vbackporch = vbp; - bt->pixelclock = htotal * vtotal * fps; - - if (bt->interlaced == V4L2_DV_INTERLACED) { - bt->height *= 2; - bt->il_vsync = bt->vsync + 1; - bt->pixelclock /= 2; + if ((bt->pixelclock > 300000000 && csi->rk628->version >= RK628F_VERSION) || + (bt->width > 2048 && csi->plat_data->tx_mode == DSI_MODE)) { + v4l2_info(sd, "rk628f detect pixclk more than 300M, use dual mipi mode\n"); + csi->rk628->dual_mipi = true; + } else { + v4l2_info(sd, "pixclk less than 300M, use single mipi mode\n"); + csi->rk628->dual_mipi = false; } - if (vact == 1080 && vtotal > 1500) - goto __retry; - - v4l2_dbg(1, debug, sd, "SCDC_REGS1:%#x, act:%dx%d, total:%dx%d, fps:%d, pixclk:%llu\n", - status, hact, vact, htotal, vtotal, fps, bt->pixelclock); v4l2_dbg(1, debug, sd, "hfp:%d, hs:%d, hbp:%d, vfp:%d, vs:%d, vbp:%d, interlace:%d\n", bt->hfrontporch, bt->hsync, bt->hbackporch, bt->vfrontporch, bt->vsync, bt->vbackporch, bt->interlaced); @@ -476,10 +447,22 @@ __retry: if (csi->scaler_en) *timings = csi->timings; - return 0; + return ret; -TIMING_ERR: - return -ENOLCK; +} + +static void rk628_hdmirx_plugout(struct v4l2_subdev *sd) +{ + struct rk628_csi *csi = to_csi(sd); + + enable_stream(sd, false); + csi->nosignal = true; + rk628_csi_enable_interrupts(sd, false); + cancel_delayed_work(&csi->delayed_work_res_change); + rk628_hdmirx_audio_cancel_work_audio(csi->audio_info, true); + rk628_hdmirx_hpd_ctrl(sd, false); + rk628_hdmirx_inno_phy_power_off(sd); + rk628_hdmirx_controller_reset(csi->rk628); } static void rk628_hdmirx_config_all(struct v4l2_subdev *sd) @@ -487,11 +470,17 @@ static void rk628_hdmirx_config_all(struct v4l2_subdev *sd) int ret; struct rk628_csi *csi = to_csi(sd); - rk628_hdmirx_controller_setup(csi->rk628); ret = rk628_hdmirx_phy_setup(sd); - if (ret >= 0) { + if (ret >= 0 && !rk628_hdmirx_scdc_ced_err(csi->rk628)) { rk628_csi_format_change(sd); csi->nosignal = false; + return; + } + + if (ret < 0 || rk628_hdmirx_scdc_ced_err(csi->rk628)) { + rk628_hdmirx_plugout(sd); + schedule_delayed_work(&csi->delayed_work_enable_hotplug, + msecs_to_jiffies(800)); } } @@ -504,6 +493,7 @@ static void rk628_csi_delayed_work_enable_hotplug(struct work_struct *work) bool plugin; mutex_lock(&csi->confctl_mutex); + rk628_set_bg_enable(csi->rk628, false); csi->avi_rcv_rdy = false; plugin = tx_5v_power_present(sd); v4l2_ctrl_s_ctrl(csi->detect_tx_5v_ctrl, plugin); @@ -512,24 +502,16 @@ static void rk628_csi_delayed_work_enable_hotplug(struct work_struct *work) rk628_csi_enable_interrupts(sd, false); rk628_hdmirx_audio_setup(csi->audio_info); rk628_hdmirx_set_hdcp(csi->rk628, &csi->hdcp, csi->enable_hdcp); + rk628_hdmirx_controller_setup(csi->rk628); rk628_hdmirx_hpd_ctrl(sd, true); rk628_hdmirx_config_all(sd); rk628_csi_enable_interrupts(sd, true); rk628_i2c_update_bits(csi->rk628, GRF_SYSTEM_CON0, SW_I2S_DATA_OEN_MASK, SW_I2S_DATA_OEN(0)); } else { - rk628_csi_enable_interrupts(sd, false); - enable_stream(sd, false); - cancel_delayed_work(&csi->delayed_work_res_change); - rk628_hdmirx_audio_cancel_work_audio(csi->audio_info, true); - rk628_hdmirx_hpd_ctrl(sd, false); - rk628_hdmirx_phy_power_off(sd); - rk628_hdmirx_controller_reset(sd); - csi->nosignal = true; + rk628_hdmirx_plugout(sd); } mutex_unlock(&csi->confctl_mutex); - if (csi->plat_data->tx_mode == DSI_MODE && plugin) - enable_stream(sd, true); } static int rk628_check_resulotion_change(struct v4l2_subdev *sd) @@ -540,6 +522,9 @@ static int rk628_check_resulotion_change(struct v4l2_subdev *sd) u32 old_htotal, old_vtotal; struct v4l2_bt_timings *bt = &csi->src_timings.bt; + if (csi->rk628->version >= RK628F_VERSION) + return 1; + rk628_i2c_read(csi->rk628, HDMI_RX_MD_HT1, &val); htotal = (val >> 16) & 0xffff; rk628_i2c_read(csi->rk628, HDMI_RX_MD_VTL, &val); @@ -566,32 +551,42 @@ static void rk628_delayed_work_res_change(struct work_struct *work) bool plugin; mutex_lock(&csi->confctl_mutex); + enable_stream(sd, false); + csi->nosignal = true; csi->avi_rcv_rdy = false; plugin = tx_5v_power_present(sd); v4l2_dbg(1, debug, sd, "%s: 5v_det:%d\n", __func__, plugin); if (plugin) { if (rk628_check_resulotion_change(sd)) { v4l2_dbg(1, debug, sd, "res change, recfg ctrler and phy!\n"); - rk628_hdmirx_audio_cancel_work_audio(csi->audio_info, true); - rk628_hdmirx_phy_power_off(sd); - rk628_hdmirx_controller_reset(sd); - rk628_hdmirx_audio_setup(csi->audio_info); - rk628_hdmirx_set_hdcp(csi->rk628, &csi->hdcp, csi->enable_hdcp); - rk628_hdmirx_hpd_ctrl(sd, true); - rk628_hdmirx_config_all(sd); - rk628_csi_enable_interrupts(sd, true); - rk628_i2c_update_bits(csi->rk628, GRF_SYSTEM_CON0, - SW_I2S_DATA_OEN_MASK, - SW_I2S_DATA_OEN(0)); + if (csi->rk628->version >= RK628F_VERSION) { + rk628_csi_enable_interrupts(sd, false); + rk628_hdmirx_audio_cancel_work_audio(csi->audio_info, true); + rk628_hdmirx_hpd_ctrl(sd, false); + rk628_hdmirx_inno_phy_power_off(sd); + rk628_hdmirx_controller_reset(csi->rk628); + schedule_delayed_work(&csi->delayed_work_enable_hotplug, + msecs_to_jiffies(800)); + } else { + rk628_hdmirx_audio_cancel_work_audio(csi->audio_info, true); + rk628_hdmirx_inno_phy_power_off(sd); + rk628_hdmirx_controller_reset(csi->rk628); + rk628_hdmirx_audio_setup(csi->audio_info); + rk628_hdmirx_set_hdcp(csi->rk628, &csi->hdcp, csi->enable_hdcp); + rk628_hdmirx_controller_setup(csi->rk628); + rk628_hdmirx_hpd_ctrl(sd, true); + rk628_hdmirx_config_all(sd); + rk628_csi_enable_interrupts(sd, true); + rk628_i2c_update_bits(csi->rk628, GRF_SYSTEM_CON0, + SW_I2S_DATA_OEN_MASK, + SW_I2S_DATA_OEN(0)); + } } else { rk628_csi_format_change(sd); - csi->nosignal = false; rk628_csi_enable_interrupts(sd, true); } } mutex_unlock(&csi->confctl_mutex); - if (csi->plat_data->tx_mode == DSI_MODE && plugin) - rk628_dsi_enable(sd); } static void rk628_hdmirx_hpd_ctrl(struct v4l2_subdev *sd, bool en) @@ -643,44 +638,102 @@ static int rk628_csi_update_controls(struct v4l2_subdev *sd) return ret; } -static void rk62_csi_reset(struct v4l2_subdev *sd) +static void rk628_csi0_cru_reset(struct v4l2_subdev *sd) { struct rk628_csi *csi = to_csi(sd); rk628_control_assert(csi->rk628, RGU_CSI); udelay(10); rk628_control_deassert(csi->rk628, RGU_CSI); +} + +static void rk628_csi1_cru_reset(struct v4l2_subdev *sd) +{ + struct rk628_csi *csi = to_csi(sd); + + rk628_control_assert(csi->rk628, RGU_CSI1); + udelay(10); + rk628_control_deassert(csi->rk628, RGU_CSI1); +} + +static void rk628_csi_soft_reset(struct v4l2_subdev *sd) +{ + struct rk628_csi *csi = to_csi(sd); rk628_i2c_write(csi->rk628, CSITX_SYS_CTRL0_IMD, 0x1); usleep_range(1000, 1100); rk628_i2c_write(csi->rk628, CSITX_SYS_CTRL0_IMD, 0x0); + + if (csi->rk628->version >= RK628F_VERSION) { + rk628_i2c_write(csi->rk628, CSITX1_SYS_CTRL0_IMD, 0x1); + usleep_range(1000, 1100); + rk628_i2c_write(csi->rk628, CSITX1_SYS_CTRL0_IMD, 0x0); + } } static void enable_csitx(struct v4l2_subdev *sd) { u32 i, ret, val; + u32 val_csi1 = 0; struct rk628_csi *csi = to_csi(sd); + //enable dphy1 and split mode + rk628_i2c_update_bits(csi->rk628, GRF_SYSTEM_CON3, GRF_DPHY_CH1_EN_MASK, + csi->rk628->dual_mipi ? GRF_DPHY_CH1_EN(1) : 0); + rk628_i2c_update_bits(csi->rk628, GRF_POST_PROC_CON, SW_SPLIT_EN, + csi->rk628->dual_mipi ? SW_SPLIT_EN : 0); + rk628_csi_set_csi(sd); for (i = 0; i < CSITX_ERR_RETRY_TIMES; i++) { - rk628_csi_set_csi(sd); + if (i) { + rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, + CSITX_EN_MASK, CSITX_EN(0)); + if (csi->rk628->version >= RK628F_VERSION) + rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN, + CSITX_EN_MASK, CSITX_EN(0)); + rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE); + if (csi->rk628->version >= RK628F_VERSION) + rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE); + + usleep_range(5000, 5500); + rk628_csi_soft_reset(sd); + usleep_range(5000, 5500); + } + rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, DPHY_EN_MASK | CSITX_EN_MASK, DPHY_EN(1) | CSITX_EN(1)); rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD); - msleep(40); + if (csi->rk628->version >= RK628F_VERSION) { + rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN, + DPHY_EN_MASK | + CSITX_EN_MASK, + DPHY_EN(1) | + CSITX_EN(1)); + rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE_IMD); + } + rk628_i2c_write(csi->rk628, CSITX_ERR_INTR_CLR_IMD, 0xffffffff); - rk628_i2c_update_bits(csi->rk628, CSITX_SYS_CTRL1, + if (csi->rk628->version <= RK628D_VERSION) + rk628_i2c_update_bits(csi->rk628, CSITX_SYS_CTRL1, BYPASS_SELECT_MASK, BYPASS_SELECT(0)); rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD); - msleep(40); + + if (csi->rk628->version >= RK628F_VERSION) { + rk628_i2c_write(csi->rk628, CSITX1_ERR_INTR_CLR_IMD, 0xffffffff); + rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE_IMD); + } + msleep(50); ret = rk628_i2c_read(csi->rk628, CSITX_ERR_INTR_RAW_STATUS_IMD, &val); - if (!ret && !val) + if (csi->rk628->version >= RK628F_VERSION) + ret |= rk628_i2c_read(csi->rk628, + CSITX1_ERR_INTR_RAW_STATUS_IMD, &val_csi1); + if (!ret && !val && !val_csi1) break; - v4l2_err(sd, "%s csitx err, retry:%d, err status:%#x, ret:%d\n", - __func__, i, val, ret); + v4l2_err(sd, "%s csitx err, retry:%d, err status csi0:%#x, csi1:%#x, ret:%d\n", + __func__, i, val, val_csi1, ret); } } @@ -698,20 +751,25 @@ static void rk628_dsi_set_scs(struct rk628_csi *csi) video_fmt = (val & VIDEO_FORMAT_MASK) >> 5; v4l2_info(&csi->sd, "%s PDEC_AVI_PB:%#x, video format:%d\n", __func__, val, video_fmt); - if (video_fmt) { - if (csi->dsi.vid_mode == VIDEO_MODE) - rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, - SW_Y2R_EN(1) | SW_YUV2VYU_SWP(1)); - else - rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, - SW_Y2R_EN(1) | SW_YUV2VYU_SWP(0)); + if (csi->rk628->version == RK628D_VERSION) { + if (video_fmt) { + if (csi->dsi.vid_mode == VIDEO_MODE) + rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, + SW_Y2R_EN(1) | SW_YUV2VYU_SWP(1)); + else + rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, + SW_Y2R_EN(1) | SW_YUV2VYU_SWP(0)); + } else { + if (csi->dsi.vid_mode == VIDEO_MODE) + rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, + SW_Y2R_EN(0) | SW_YUV2VYU_SWP(1)); + else + rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, + SW_Y2R_EN(0) | SW_YUV2VYU_SWP(0)); + } } else { - if (csi->dsi.vid_mode == VIDEO_MODE) - rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, - SW_Y2R_EN(0) | SW_YUV2VYU_SWP(1)); - else - rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, - SW_Y2R_EN(0) | SW_YUV2VYU_SWP(0)); + rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, SW_YUV2VYU_SWP(0)); + rk628_post_process_csc_en(csi->rk628); } /* if avi packet is not stable, reset ctrl*/ @@ -750,9 +808,15 @@ static void enable_dsitx(struct v4l2_subdev *sd) /* rst for dsi0 */ rk628_control_assert(csi->rk628, RGU_DSI0); - usleep_range(20, 40); + udelay(20); rk628_control_deassert(csi->rk628, RGU_DSI0); - usleep_range(20, 40); + udelay(20); + + /* rst for dsi1 */ + rk628_control_assert(csi->rk628, RGU_DSI1); + udelay(20); + rk628_control_deassert(csi->rk628, RGU_DSI1); + udelay(20); rk628_dsi_enable(sd); } @@ -770,6 +834,28 @@ static void rk628_dsi_enable_stream(struct v4l2_subdev *sd, bool en) rk628_hdmirx_vid_enable(sd, false); rk628_i2c_write(csi->rk628, GRF_SCALER_CON0, SCL_EN(0)); + rk628_dsi_disable_stream(&csi->dsi); +} + +static void rk628_csi_disable_stream(struct v4l2_subdev *sd) +{ + struct rk628_csi *csi = to_csi(sd); + + rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, + DPHY_EN_MASK | CSITX_EN_MASK, + DPHY_EN(0) | CSITX_EN(0)); + rk628_i2c_update_bits(csi->rk628, CSITX_SYS_CTRL3_IMD, CONT_MODE_CLK_CLR_MASK, + csi->continues_clk ? CONT_MODE_CLK_CLR(1) : CONT_MODE_CLK_CLR(0)); + rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE); + + if (csi->rk628->version >= RK628F_VERSION) { + rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN, + DPHY_EN_MASK | CSITX_EN_MASK, + DPHY_EN(0) | CSITX_EN(0)); + rk628_i2c_update_bits(csi->rk628, CSITX1_SYS_CTRL3_IMD, CONT_MODE_CLK_CLR_MASK, + csi->continues_clk ? CONT_MODE_CLK_CLR(1) : CONT_MODE_CLK_CLR(0)); + rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE); + } } static void enable_stream(struct v4l2_subdev *sd, bool en) @@ -778,6 +864,13 @@ static void enable_stream(struct v4l2_subdev *sd, bool en) v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, en ? "en" : "dis"); if (en) { + if (rk628_hdmirx_scdc_ced_err(csi->rk628)) { + rk628_hdmirx_plugout(sd); + schedule_delayed_work(&csi->delayed_work_enable_hotplug, + msecs_to_jiffies(800)); + return; + } + rk628_hdmirx_vid_enable(sd, true); if (csi->plat_data->tx_mode == DSI_MODE) enable_dsitx(sd); @@ -786,13 +879,7 @@ static void enable_stream(struct v4l2_subdev *sd, bool en) } else { if (csi->plat_data->tx_mode == CSI_MODE) { rk628_hdmirx_vid_enable(sd, false); - rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, - DPHY_EN_MASK | - CSITX_EN_MASK, - DPHY_EN(0) | - CSITX_EN(0)); - rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, - CONFIG_DONE_IMD); + rk628_csi_disable_stream(sd); } else { rk628_dsi_enable_stream(sd, en); } @@ -846,15 +933,22 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) u8 video_fmt; u8 lanes = csi->csi_lanes_in_use; u8 lane_num; - u8 dphy_lane_en; u32 wc_usrdef, val; int avi_rdy; lane_num = lanes - 1; - dphy_lane_en = (1 << (lanes + 1)) - 1; + csi->rk628->dphy_lane_en = (1 << (lanes + 1)) - 1; wc_usrdef = csi->timings.bt.width * 2; - - rk62_csi_reset(sd); + if (csi->rk628->dual_mipi) + wc_usrdef = csi->timings.bt.width; + v4l2_info(sd, "%s mipi mode, word count user define: %d\n", + csi->rk628->dual_mipi ? "dual" : "single", wc_usrdef); + rk628_csi_disable_stream(sd); + usleep_range(5000, 5500); + rk628_csi0_cru_reset(sd); + if (csi->rk628->version >= RK628F_VERSION) + rk628_csi1_cru_reset(sd); + rk628_mipi_dphy_reset(csi->rk628); rk628_post_process_setup(sd); if (csi->txphy_pwron) { @@ -869,7 +963,8 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) v4l2_dbg(2, debug, sd, "%s: txphy power on!\n", __func__); usleep_range(1000, 1500); - rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, + if (csi->rk628->version <= RK628D_VERSION) { + rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, VOP_UV_SWAP_MASK | VOP_YUV422_EN_MASK | VOP_P2_EN_MASK | @@ -882,9 +977,30 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) LANE_NUM(lane_num) | DPHY_EN(0) | CSITX_EN(0)); - rk628_i2c_update_bits(csi->rk628, CSITX_SYS_CTRL1, + rk628_i2c_update_bits(csi->rk628, CSITX_SYS_CTRL1, BYPASS_SELECT_MASK, BYPASS_SELECT(1)); + } else { + rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, + VOP_UV_SWAP_MASK | + VOP_YUV422_EN_MASK | + VOP_YUV422_MODE_MASK | + VOP_P2_EN_MASK | + LANE_NUM_MASK | + DPHY_EN_MASK | + CSITX_EN_MASK, + VOP_UV_SWAP(0) | + VOP_YUV422_EN(1) | + VOP_YUV422_MODE(2) | + VOP_P2_EN(1) | + LANE_NUM(lane_num) | + DPHY_EN(0) | + CSITX_EN(0)); + rk628_i2c_update_bits(csi->rk628, CSITX_SYS_CTRL1, + BYPASS_SELECT_MASK, + BYPASS_SELECT(0)); + } + rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD); rk628_i2c_write(csi->rk628, CSITX_SYS_CTRL2, VOP_WHOLE_FRM_EN | VSYNC_ENABLE); if (csi->continues_clk) @@ -913,9 +1029,67 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) VOP_PATH_EN(1)); rk628_i2c_update_bits(csi->rk628, CSITX_DPHY_CTRL, CSI_DPHY_EN_MASK, - CSI_DPHY_EN(dphy_lane_en)); + CSI_DPHY_EN(csi->rk628->dphy_lane_en)); + rk628_i2c_update_bits(csi->rk628, CSITX_VOP_FILTER_CTRL, + VOP_FILTER_EN_MASK | VOP_FILTER_MASK, + VOP_FILTER_EN(1) | VOP_FILTER(3)); rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD); - v4l2_dbg(1, debug, sd, "%s csi cofig done\n", __func__); + v4l2_dbg(1, debug, sd, "%s csi config done\n", __func__); + + if (csi->rk628->version >= RK628F_VERSION) { + rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN, + VOP_UV_SWAP_MASK | + VOP_YUV422_EN_MASK | + VOP_YUV422_MODE_MASK | + VOP_P2_EN_MASK | + LANE_NUM_MASK | + DPHY_EN_MASK | + CSITX_EN_MASK, + VOP_UV_SWAP(0) | + VOP_YUV422_EN(1) | + VOP_YUV422_MODE(2) | + VOP_P2_EN(1) | + LANE_NUM(lane_num) | + DPHY_EN(0) | + CSITX_EN(0)); + rk628_i2c_update_bits(csi->rk628, CSITX1_SYS_CTRL1, + BYPASS_SELECT_MASK, + BYPASS_SELECT(0)); + rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE_IMD); + rk628_i2c_write(csi->rk628, CSITX1_SYS_CTRL2, VOP_WHOLE_FRM_EN | VSYNC_ENABLE); + if (csi->continues_clk) + rk628_i2c_update_bits(csi->rk628, CSITX1_SYS_CTRL3_IMD, + CONT_MODE_CLK_CLR_MASK | + CONT_MODE_CLK_SET_MASK | + NON_CONTINUOUS_MODE_MASK, + CONT_MODE_CLK_CLR(0) | + CONT_MODE_CLK_SET(1) | + NON_CONTINUOUS_MODE(0)); + else + rk628_i2c_update_bits(csi->rk628, CSITX1_SYS_CTRL3_IMD, + CONT_MODE_CLK_CLR_MASK | + CONT_MODE_CLK_SET_MASK | + NON_CONTINUOUS_MODE_MASK, + CONT_MODE_CLK_CLR(0) | + CONT_MODE_CLK_SET(0) | + NON_CONTINUOUS_MODE(1)); + + rk628_i2c_write(csi->rk628, CSITX1_VOP_PATH_CTRL, + VOP_WC_USERDEFINE(wc_usrdef) | + VOP_DT_USERDEFINE(YUV422_8BIT) | + VOP_PIXEL_FORMAT(0) | + VOP_WC_USERDEFINE_EN(1) | + VOP_DT_USERDEFINE_EN(1) | + VOP_PATH_EN(1)); + rk628_i2c_update_bits(csi->rk628, CSITX1_DPHY_CTRL, + CSI_DPHY_EN_MASK, + CSI_DPHY_EN(csi->rk628->dphy_lane_en)); + rk628_i2c_update_bits(csi->rk628, CSITX1_VOP_FILTER_CTRL, + VOP_FILTER_EN_MASK | VOP_FILTER_MASK, + VOP_FILTER_EN(1) | VOP_FILTER(3)); + rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE_IMD); + v4l2_dbg(1, debug, sd, "%s csi1 config done\n", __func__); + } mutex_lock(&csi->confctl_mutex); avi_rdy = rk628_is_avi_ready(csi->rk628, csi->avi_rcv_rdy); @@ -925,18 +1099,22 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) video_fmt = (val & VIDEO_FORMAT_MASK) >> 5; v4l2_dbg(1, debug, &csi->sd, "%s PDEC_AVI_PB:%#x, video format:%d\n", __func__, val, video_fmt); - if (video_fmt) { - /* yuv data: cfg SW_YUV2VYU_SWP */ - rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, + if (csi->rk628->version == RK628D_VERSION) { + if (video_fmt) { + /* yuv data: cfg SW_YUV2VYU_SWP */ + rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, SW_YUV2VYU_SWP(1) | SW_R2Y_EN(0)); - } else { - /* rgb data: cfg SW_R2Y_EN */ - rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, + } else { + /* rgb data: cfg SW_R2Y_EN */ + rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, SW_YUV2VYU_SWP(0) | - SW_R2Y_EN(1)); + SW_R2Y_EN(1) | SW_R2Y_CSC_MODE(2)); + } + } else { + rk628_i2c_write(csi->rk628, GRF_CSC_CTRL_CON, SW_YUV2VYU_SWP(1)); + rk628_post_process_csc_en(csi->rk628); } - /* if avi packet is not stable, reset ctrl*/ if (!avi_rdy) { csi->nosignal = true; @@ -944,7 +1122,7 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd) } } -static int rk628_hdmirx_phy_power_on(struct v4l2_subdev *sd) +static int rk628_hdmirx_inno_phy_power_on(struct v4l2_subdev *sd) { struct rk628_csi *csi = to_csi(sd); int ret, f; @@ -985,10 +1163,13 @@ static int rk628_hdmirx_phy_power_on(struct v4l2_subdev *sd) return ret; } -static int rk628_hdmirx_phy_power_off(struct v4l2_subdev *sd) +static int rk628_hdmirx_inno_phy_power_off(struct v4l2_subdev *sd) { struct rk628_csi *csi = to_csi(sd); + if (csi->rk628->version >= RK628F_VERSION) + return 0; + if (csi->rxphy_pwron) { v4l2_dbg(1, debug, sd, "rxphy power off!\n"); rk628_rxphy_power_off(csi->rk628); @@ -1017,25 +1198,14 @@ static void rk628_hdmirx_vid_enable(struct v4l2_subdev *sd, bool en) } } -static void rk628_hdmirx_controller_reset(struct v4l2_subdev *sd) -{ - struct rk628_csi *csi = to_csi(sd); - - v4l2_dbg(1, debug, sd, "%s reset hdmirx_controller\n", __func__); - rk628_control_assert(csi->rk628, RGU_HDMIRX_PON); - udelay(10); - rk628_control_deassert(csi->rk628, RGU_HDMIRX_PON); - udelay(10); - rk628_i2c_write(csi->rk628, HDMI_RX_DMI_SW_RST, 0x000101ff); - rk628_i2c_write(csi->rk628, HDMI_RX_DMI_DISABLE_IF, 0x00000000); - rk628_i2c_write(csi->rk628, HDMI_RX_DMI_DISABLE_IF, 0x0000017f); - rk628_i2c_write(csi->rk628, HDMI_RX_DMI_DISABLE_IF, 0x0001017f); -} - static bool rk628_rcv_supported_res(struct v4l2_subdev *sd, u32 width, u32 height) { u32 i; + struct rk628_csi *csi = to_csi(sd); + + if (csi->rk628->version >= RK628F_VERSION) + return true; for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { if ((supported_modes[i].width == width) && @@ -1057,10 +1227,13 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) u32 i, cnt, val; u32 width, height, frame_width, frame_height, status; struct rk628_csi *csi = to_csi(sd); - int ret; + int ret = 0; for (i = 0; i < RXPHY_CFG_MAX_TIMES; i++) { - ret = rk628_hdmirx_phy_power_on(sd); + if (csi->rk628->version < RK628F_VERSION) + ret = rk628_hdmirx_inno_phy_power_on(sd); + else + rk628_hdmirx_verisyno_phy_power_on(csi->rk628); if (ret < 0) { msleep(50); continue; @@ -1068,6 +1241,7 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) cnt = 0; do { + msleep(20); cnt++; rk628_i2c_read(csi->rk628, HDMI_RX_MD_HACT_PX, &val); width = val & 0xffff; @@ -1083,7 +1257,7 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) __func__, width, height, frame_width, frame_height, status, cnt); rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_STS, &val); - if (val & DVI_DET) + if (csi->rk628->version < RK628F_VERSION && (val & DVI_DET)) dev_info(csi->dev, "DVI mode detected\n"); if (!tx_5v_power_present(sd)) { @@ -1093,15 +1267,17 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) if (cnt >= 15) break; - } while (((status & 0xfff) != 0xf00) || + } while (((status & 0xfff) < 0xf00) || (!rk628_rcv_supported_res(sd, width, height))); - if (((status & 0xfff) != 0xf00) || + if (((status & 0xfff) < 0xf00) || (!rk628_rcv_supported_res(sd, width, height))) { v4l2_err(sd, "%s hdmi rxphy lock failed, retry:%d\n", __func__, i); continue; } else { + if (csi->rk628->version >= RK628F_VERSION) + rk628_hdmirx_phy_prepclk_cfg(csi->rk628); break; } } @@ -1116,6 +1292,8 @@ static void rk628_csi_initial_setup(struct v4l2_subdev *sd) { struct rk628_csi *csi = to_csi(sd); struct v4l2_subdev_edid def_edid; + u32 mask = SW_OUTPUT_MODE_MASK; + u32 val = SW_OUTPUT_MODE(OUTPUT_MODE_CSI); /* selete int io function */ rk628_i2c_write(csi->rk628, GRF_GPIO3AB_SEL_CON, 0x30002000); @@ -1127,45 +1305,72 @@ static void rk628_csi_initial_setup(struct v4l2_subdev *sd) /* I2SM0D0 */ rk628_i2c_write(csi->rk628, GRF_GPIO0AB_SEL_CON, HIWORD_UPDATE(0x1, 5, 4)); /* hdmirx int en */ - rk628_i2c_write(csi->rk628, GRF_INTR0_EN, 0x01000100); + if (csi->rk628->version >= RK628F_VERSION) + rk628_i2c_write(csi->rk628, GRF_INTR0_EN, 0x02000200); + else + rk628_i2c_write(csi->rk628, GRF_INTR0_EN, 0x01000100); udelay(10); rk628_control_assert(csi->rk628, RGU_HDMIRX); rk628_control_assert(csi->rk628, RGU_HDMIRX_PON); rk628_control_assert(csi->rk628, RGU_CSI); + if (csi->rk628->version >= RK628F_VERSION) + rk628_control_assert(csi->rk628, RGU_CSI1); udelay(10); rk628_control_deassert(csi->rk628, RGU_HDMIRX); rk628_control_deassert(csi->rk628, RGU_HDMIRX_PON); rk628_control_deassert(csi->rk628, RGU_CSI); + if (csi->rk628->version >= RK628F_VERSION) + rk628_control_deassert(csi->rk628, RGU_CSI1); udelay(10); + if (csi->rk628->version >= RK628F_VERSION) { + mask = SW_OUTPUT_COMBTX_MODE_MASK; + val = SW_OUTPUT_COMBTX_MODE(OUTPUT_MODE_CSI - 1); + } rk628_i2c_update_bits(csi->rk628, GRF_SYSTEM_CON0, SW_INPUT_MODE_MASK | - SW_OUTPUT_MODE_MASK | + mask | SW_EFUSE_HDCP_EN_MASK | SW_HSYNC_POL_MASK | SW_VSYNC_POL_MASK, SW_INPUT_MODE(INPUT_MODE_HDMI) | - SW_OUTPUT_MODE(OUTPUT_MODE_CSI) | + val | SW_EFUSE_HDCP_EN(0) | SW_HSYNC_POL(1) | SW_VSYNC_POL(1)); - rk628_hdmirx_controller_reset(sd); + rk628_hdmirx_controller_reset(csi->rk628); def_edid.pad = 0; def_edid.start_block = 0; def_edid.blocks = 2; - def_edid.edid = edid_init_data; + if (csi->rk628->version >= RK628F_VERSION) + def_edid.edid = rk628f_edid_init_data; + else + def_edid.edid = edid_init_data; rk628_csi_s_edid(sd, &def_edid); rk628_hdmirx_set_hdcp(csi->rk628, &csi->hdcp, false); + if (csi->rk628->version >= RK628F_VERSION) { + csi->rk628->mipi_timing[0] = rk628f_csi0_mipi; + csi->rk628->mipi_timing[1] = rk628f_csi1_mipi; + } else { + csi->rk628->mipi_timing[0] = rk628d_csi_mipi; + } + + if (csi->plat_data->tx_mode == DSI_MODE) { + csi->rk628->mipi_timing[0] = rk628f_dsi0_mipi; + csi->rk628->mipi_timing[1] = rk628f_dsi0_mipi; + } + + csi->rk628->dphy_lane_en = 0x1f; if (csi->plat_data->tx_mode == CSI_MODE) { - mipi_dphy_reset(csi->rk628); + rk628_mipi_dphy_reset(csi->rk628); mipi_dphy_power_on(csi); } csi->txphy_pwron = true; if (tx_5v_power_present(sd)) - schedule_delayed_work(&csi->delayed_work_enable_hotplug, msecs_to_jiffies(1000)); + schedule_delayed_work(&csi->delayed_work_enable_hotplug, msecs_to_jiffies(4000)); } static void rk628_csi_format_change(struct v4l2_subdev *sd) @@ -1196,7 +1401,7 @@ static void rk628_csi_enable_interrupts(struct v4l2_subdev *sd, bool en) u32 pdec_mask = 0, md_mask = 0; struct rk628_csi *csi = to_csi(sd); - pdec_mask |= AVI_RCV_ENSET; + pdec_mask = AVI_RCV_ENSET | AVI_CKS_CHG_ICLR; md_mask = VACT_LIN_ENSET | HACT_PIX_ENSET | HS_CLK_ENSET | DE_ACTIVITY_ENSET | VS_ACT_ENSET | HS_ACT_ENSET; v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, en ? "en" : "dis"); @@ -1219,42 +1424,56 @@ static void rk628_csi_enable_interrupts(struct v4l2_subdev *sd, bool en) v4l2_dbg(1, debug, sd, "%s MD_IEN:%#x, PDEC_IEN:%#x\n", __func__, md_ien, pdec_ien); } -static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +static void rk628_work_isr(struct work_struct *work) { + struct rk628_csi *csi = container_of(work, struct rk628_csi, work_isr); + struct v4l2_subdev *sd = &csi->sd; u32 md_ints, pdec_ints, fifo_ints, hact, vact; bool plugin; - struct rk628_csi *csi = to_csi(sd); void *audio_info = csi->audio_info; + bool handled = false; - if (handled == NULL) { - v4l2_err(sd, "handled NULL, err return!\n"); - return -EINVAL; - } + mutex_lock(&csi->rk628->rst_lock); + rk628_i2c_read(csi->rk628, HDMI_RX_MD_ISTS, &md_ints); rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_ISTS, &pdec_ints); - if (rk628_audio_ctsnints_enabled(audio_info)) { - if (pdec_ints & (ACR_N_CHG_ICLR | ACR_CTS_CHG_ICLR)) { - rk628_csi_isr_ctsn(audio_info, pdec_ints); - pdec_ints &= ~(ACR_CTS_CHG_ICLR | ACR_CTS_CHG_ICLR); - *handled = true; - } + if (csi->rk628->version >= RK628F_VERSION && + (md_ints & (VACT_LIN_ISTS | HACT_PIX_ISTS | + HS_CLK_ISTS | DE_ACTIVITY_ISTS | + VS_ACT_ISTS | HS_ACT_ISTS) || + pdec_ints & AVI_CKS_CHG_ISTS)) + rk628_set_bg_enable(csi->rk628, true); + + plugin = tx_5v_power_present(sd); + if (!plugin) { + rk628_csi_enable_interrupts(sd, false); + goto __clear_int; } - if (rk628_audio_fifoints_enabled(audio_info)) { - rk628_i2c_read(csi->rk628, HDMI_RX_AUD_FIFO_ISTS, &fifo_ints); - if (fifo_ints & 0x18) { - rk628_csi_isr_fifoints(audio_info, fifo_ints); - *handled = true; + + if (csi->rk628->version < RK628F_VERSION) { + if (rk628_audio_ctsnints_enabled(audio_info)) { + if (pdec_ints & (ACR_N_CHG_ICLR | ACR_CTS_CHG_ICLR)) { + rk628_csi_isr_ctsn(audio_info, pdec_ints); + pdec_ints &= ~(ACR_CTS_CHG_ICLR | ACR_CTS_CHG_ICLR); + handled = true; + } + } + if (rk628_audio_fifoints_enabled(audio_info)) { + rk628_i2c_read(csi->rk628, HDMI_RX_AUD_FIFO_ISTS, &fifo_ints); + if (fifo_ints & 0x18) { + rk628_csi_isr_fifoints(audio_info, fifo_ints); + handled = true; + } } } if (csi->vid_ints_en) { rk628_i2c_read(csi->rk628, HDMI_RX_MD_ISTS, &md_ints); - plugin = tx_5v_power_present(sd); v4l2_dbg(1, debug, sd, "%s: md_ints: %#x, pdec_ints:%#x, plugin: %d\n", __func__, md_ints, pdec_ints, plugin); if ((md_ints & (VACT_LIN_ISTS | HACT_PIX_ISTS | HS_CLK_ISTS | DE_ACTIVITY_ISTS | - VS_ACT_ISTS | HS_ACT_ISTS)) - && plugin) { + VS_ACT_ISTS | HS_ACT_ISTS) || + pdec_ints & AVI_CKS_CHG_ISTS)) { rk628_i2c_read(csi->rk628, HDMI_RX_MD_HACT_PX, &hact); rk628_i2c_read(csi->rk628, HDMI_RX_MD_VAL, &vact); @@ -1262,13 +1481,15 @@ static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled) __func__, hact, vact); rk628_csi_enable_interrupts(sd, false); - enable_stream(sd, false); - csi->nosignal = true; - schedule_delayed_work(&csi->delayed_work_res_change, HZ / 2); + if (csi->rk628->version < RK628F_VERSION) { + enable_stream(sd, false); + csi->nosignal = true; + } + schedule_delayed_work(&csi->delayed_work_res_change, msecs_to_jiffies(100)); v4l2_dbg(1, debug, sd, "%s: hact/vact change, md_ints: %#x\n", __func__, (u32)(md_ints & (VACT_LIN_ISTS | HACT_PIX_ISTS))); - *handled = true; + handled = true; } if ((pdec_ints & AVI_RCV_ISTS) && plugin && !csi->avi_rcv_rdy) { @@ -1279,16 +1500,35 @@ static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled) /* After get the AVI_RCV interrupt state, disable interrupt. */ rk628_i2c_write(csi->rk628, HDMI_RX_PDEC_IEN_CLR, AVI_RCV_ISTS); - *handled = true; + handled = true; } } - if (*handled != true) + if (!handled) v4l2_dbg(1, debug, sd, "%s: unhandled interrupt!\n", __func__); +__clear_int: /* clear interrupts */ rk628_i2c_write(csi->rk628, HDMI_RX_MD_ICLR, 0xffffffff); rk628_i2c_write(csi->rk628, HDMI_RX_PDEC_ICLR, 0xffffffff); - rk628_i2c_write(csi->rk628, GRF_INTR0_CLR_EN, 0x01000100); + if (csi->rk628->version >= RK628F_VERSION) + rk628_i2c_write(csi->rk628, GRF_INTR0_CLR_EN, 0x02000200); + else + rk628_i2c_write(csi->rk628, GRF_INTR0_CLR_EN, 0x01000100); + + mutex_unlock(&csi->rk628->rst_lock); +} + + +static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + struct rk628_csi *csi = to_csi(sd); + + if (handled == NULL) { + v4l2_err(sd, "handled NULL, err return!\n"); + return -EINVAL; + } + + schedule_work(&csi->work_isr); return 0; } @@ -1296,7 +1536,7 @@ static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled) static irqreturn_t rk628_csi_irq_handler(int irq, void *dev_id) { struct rk628_csi *csi = dev_id; - bool handled = false; + bool handled = true; rk628_csi_isr(&csi->sd, 0, &handled); @@ -1453,14 +1693,25 @@ static int rk628_csi_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad, return 0; } +static inline unsigned int fps_calc(const struct v4l2_bt_timings *t) +{ + if (!V4L2_DV_BT_FRAME_HEIGHT(t) || !V4L2_DV_BT_FRAME_WIDTH(t)) + return 0; + + return DIV_ROUND_CLOSEST((unsigned int)t->pixelclock, + V4L2_DV_BT_FRAME_HEIGHT(t) * V4L2_DV_BT_FRAME_WIDTH(t)); +} + static int rk628_csi_s_stream(struct v4l2_subdev *sd, int enable) { struct rk628_csi *csi = to_csi(sd); - if (csi->plat_data->tx_mode == CSI_MODE) - enable_stream(sd, enable); - else - rk628_dsi_enable_stream(sd, enable); + enable_stream(sd, enable); + + v4l2_info(sd, "%s: on: %d, %dx%d@%d\n", __func__, enable, + csi->timings.bt.width, + csi->timings.bt.height, + fps_calc(&csi->timings.bt)); return 0; } @@ -1534,23 +1785,46 @@ static int rk628_csi_get_fmt(struct v4l2_subdev *sd, V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; mutex_unlock(&csi->confctl_mutex); - if (((csi->timings.bt.width == 3840) && (csi->timings.bt.height == 2160)) || - csi->csi_lanes_in_use <= 2) { - v4l2_dbg(1, debug, sd, - "%s res wxh:%dx%d, link freq:%llu, pixrate:%u\n", - __func__, csi->timings.bt.width, csi->timings.bt.height, - link_freq_menu_items[1], RK628_CSI_PIXEL_RATE_HIGH); - __v4l2_ctrl_s_ctrl(csi->link_freq, 1); - __v4l2_ctrl_s_ctrl_int64(csi->pixel_rate, - RK628_CSI_PIXEL_RATE_HIGH); + if (csi->plat_data->tx_mode == CSI_MODE) { + if (csi->timings.bt.pixelclock > 150000000 || csi->csi_lanes_in_use <= 2) { + v4l2_dbg(1, debug, sd, + "%s res wxh:%dx%d, link freq:%llu, pixrate:%u\n", + __func__, csi->timings.bt.width, csi->timings.bt.height, + link_freq_menu_items[1], RK628_CSI_PIXEL_RATE_HIGH); + __v4l2_ctrl_s_ctrl(csi->link_freq, 1); + __v4l2_ctrl_s_ctrl_int64(csi->pixel_rate, + RK628_CSI_PIXEL_RATE_HIGH); + } else { + v4l2_dbg(1, debug, sd, + "%s res wxh:%dx%d, link freq:%llu, pixrate:%u\n", + __func__, csi->timings.bt.width, csi->timings.bt.height, + link_freq_menu_items[0], RK628_CSI_PIXEL_RATE_LOW); + __v4l2_ctrl_s_ctrl(csi->link_freq, 0); + __v4l2_ctrl_s_ctrl_int64(csi->pixel_rate, + RK628_CSI_PIXEL_RATE_LOW); + } } else { - v4l2_dbg(1, debug, sd, - "%s res wxh:%dx%d, link freq:%llu, pixrate:%u\n", - __func__, csi->timings.bt.width, csi->timings.bt.height, - link_freq_menu_items[0], RK628_CSI_PIXEL_RATE_LOW); - __v4l2_ctrl_s_ctrl(csi->link_freq, 0); - __v4l2_ctrl_s_ctrl_int64(csi->pixel_rate, - RK628_CSI_PIXEL_RATE_LOW); + u32 rate; + + csi->dsi.rk628 = csi->rk628; + csi->dsi.timings = csi->timings; + rate = rk628_dsi_get_lane_rate_mbps(&csi->dsi); + v4l2_dbg(1, debug, sd, "%s mipi bitrate:%u mbps\n", __func__, rate); + + if (rate > 1300) { + csi->rk628->mipi_timing[0] = rk628f_dsi0_mipi; + csi->rk628->mipi_timing[1] = rk628f_dsi0_mipi; + __v4l2_ctrl_s_ctrl(csi->link_freq, 2); + } else if (rate <= 1300 && rate > 700) { + csi->rk628->mipi_timing[0] = rk628f_csi0_mipi; + csi->rk628->mipi_timing[1] = rk628f_csi0_mipi; + __v4l2_ctrl_s_ctrl(csi->link_freq, 1); + } else { + csi->rk628->mipi_timing[0] = rk628f_csi0_mipi; + csi->rk628->mipi_timing[1] = rk628f_csi0_mipi; + __v4l2_ctrl_s_ctrl(csi->link_freq, 0); + } + __v4l2_ctrl_s_ctrl_int64(csi->pixel_rate, RK628_CSI_PIXEL_RATE_HIGH); } v4l2_dbg(1, debug, sd, "%s: fmt code:%d, w:%d, h:%d, field code:%d\n", @@ -1738,7 +2012,7 @@ static int rk628_csi_s_edid(struct v4l2_subdev *sd, csi->edid_blocks_written = edid->blocks; udelay(100); - if (tx_5v_power_present(sd)) + if (tx_5v_power_present(sd) && csi->rk628->version < RK628F_VERSION) rk628_hdmirx_hpd_ctrl(sd, true); return 0; @@ -1772,6 +2046,7 @@ static long rk628_csi_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) struct rk628_csi *csi = to_csi(sd); long ret = 0; struct rkmodule_csi_dphy_param *dphy_param; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -1794,6 +2069,22 @@ static long rk628_csi_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) v4l2_dbg(1, debug, sd, "sensor get dphy param\n"); break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = (struct rkmodule_capture_info *)arg; + if (csi->rk628->dual_mipi) { + v4l2_info(sd, "set dual mipi mode\n"); + capture_info->mode = RKMODULE_MULTI_DEV_COMBINE_ONE; + capture_info->multi_dev = csi->multi_dev_info; + } else { + capture_info->mode = 0; + } + break; + case RKMODULE_GET_CSI_DSI_INFO: + if (csi->plat_data->tx_mode == DSI_MODE) + *(int *)arg = RKMODULE_DSI_INPUT; + else + *(int *)arg = RKMODULE_CSI_INPUT; + break; default: ret = -ENOIOCTLCMD; break; @@ -1808,25 +2099,37 @@ static int mipi_dphy_power_on(struct rk628_csi *csi) u32 bus_width, mask; struct v4l2_subdev *sd = &csi->sd; - if ((csi->timings.bt.width == 3840 && csi->timings.bt.height == 2160) || - csi->csi_lanes_in_use <= 2) { + if (csi->timings.bt.pixelclock > 150000000 || csi->csi_lanes_in_use <= 2) { csi->lane_mbps = MIPI_DATARATE_MBPS_HIGH; } else { csi->lane_mbps = MIPI_DATARATE_MBPS_LOW; } - + if (csi->rk628->dual_mipi) + csi->lane_mbps = MIPI_DATARATE_MBPS_HIGH; bus_width = csi->lane_mbps << 8; bus_width |= COMBTXPHY_MODULEA_EN; + if (csi->rk628->version >= RK628F_VERSION) + bus_width |= COMBTXPHY_MODULEB_EN; v4l2_dbg(1, debug, sd, "%s mipi bitrate:%llu mbps\n", __func__, csi->lane_mbps); rk628_txphy_set_bus_width(csi->rk628, bus_width); rk628_txphy_set_mode(csi->rk628, PHY_MODE_VIDEO_MIPI); - if (csi->lane_mbps == MIPI_DATARATE_MBPS_HIGH) - mipi_dphy_init_hsmanual(csi->rk628, true); - else - mipi_dphy_init_hsmanual(csi->rk628, false); - mipi_dphy_init_hsfreqrange(csi->rk628, csi->lane_mbps); + rk628_mipi_dphy_init_hsfreqrange(csi->rk628, csi->lane_mbps, 0); + if (csi->rk628->version >= RK628F_VERSION) + rk628_mipi_dphy_init_hsfreqrange(csi->rk628, csi->lane_mbps, 1); + + if (csi->rk628->dual_mipi) { + rk628_mipi_dphy_init_hsmanual(csi->rk628, true, 0); + rk628_mipi_dphy_init_hsmanual(csi->rk628, true, 1); + } else if (csi->lane_mbps == MIPI_DATARATE_MBPS_HIGH && !csi->rk628->dual_mipi) { + rk628_mipi_dphy_init_hsmanual(csi->rk628, true, 0); + rk628_mipi_dphy_init_hsmanual(csi->rk628, false, 1); + } else { + rk628_mipi_dphy_init_hsmanual(csi->rk628, false, 0); + rk628_mipi_dphy_init_hsmanual(csi->rk628, false, 1); + } + usleep_range(1500, 2000); rk628_txphy_power_on(csi->rk628); @@ -1837,7 +2140,13 @@ static int mipi_dphy_power_on(struct rk628_csi *csi) dev_err(csi->dev, "PHY is not locked\n"); return -1; } - + if (csi->rk628->version >= RK628F_VERSION) { + rk628_i2c_read(csi->rk628, CSITX1_CSITX_STATUS1, &val); + if ((val & mask) != mask) { + dev_err(csi->dev, "PHY1 is not locked\n"); + return -1; + } + } udelay(10); return 0; @@ -1857,6 +2166,7 @@ static long rk628_csi_compat_ioctl32(struct v4l2_subdev *sd, long ret; int *seq; struct rkmodule_csi_dphy_param *dphy_param; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -1918,6 +2228,35 @@ static long rk628_csi_compat_ioctl32(struct v4l2_subdev *sd, } 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 = rk628_csi_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; + case RKMODULE_GET_CSI_DSI_INFO: + seq = kzalloc(sizeof(*seq), GFP_KERNEL); + if (!seq) { + ret = -ENOMEM; + return ret; + } + ret = rk628_csi_ioctl(sd, cmd, seq); + if (!ret) { + ret = copy_to_user(up, seq, sizeof(*seq)); + if (ret) + ret = -EFAULT; + } + kfree(seq); + break; default: ret = -ENOIOCTLCMD; break; @@ -2203,6 +2542,36 @@ static struct attribute *rk628_attrs[] = { }; ATTRIBUTE_GROUPS(rk628); +static int rk628_csi_get_multi_dev_info(struct rk628_csi *csi) +{ + struct device *dev = &csi->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", + &csi->multi_dev_info.dev_idx[0]); + of_property_read_u32(multi_info_np, "dev-idx-r", + &csi->multi_dev_info.dev_idx[1]); + of_property_read_u32(multi_info_np, "combine-idx", + &csi->multi_dev_info.combine_idx[0]); + of_property_read_u32(multi_info_np, "pixel-offset", + &csi->multi_dev_info.pixel_offset); + of_property_read_u32(multi_info_np, "dev-num", + &csi->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", + csi->multi_dev_info.dev_idx[0], csi->multi_dev_info.dev_idx[1], + csi->multi_dev_info.combine_idx[0], csi->multi_dev_info.dev_num); + + return 0; +} + static int rk628_csi_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2216,6 +2585,8 @@ static int rk628_csi_probe(struct i2c_client *client, struct rk628 *rk628; unsigned long irq_flags; const struct of_device_id *match; + struct v4l2_dv_timings default_timing = + V4L2_DV_BT_CEA_640X480P59_94; dev_info(dev, "RK628 I2C driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16, @@ -2238,9 +2609,11 @@ static int rk628_csi_probe(struct i2c_client *client, match = of_match_node(rk628_csi_of_match, dev->of_node); csi->plat_data = match->data; + rk628->tx_mode = csi->plat_data->tx_mode; csi->rk628 = rk628; csi->dsi.rk628 = rk628; csi->cur_mode = &supported_modes[0]; + csi->timings = default_timing; csi->hdmirx_irq = client->irq; sd = &csi->sd; sd->dev = dev; @@ -2268,6 +2641,14 @@ static int rk628_csi_probe(struct i2c_client *client, rk628_cru_initialize(csi->rk628); + rk628_version_parse(rk628); + + if (rk628->version >= RK628F_VERSION) { + err = rk628_csi_get_multi_dev_info(csi); + if (err) + v4l2_info(sd, "get multi dev info failed, not use dual mipi mode\n"); + } + v4l2_subdev_init(sd, &rk628_csi_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; @@ -2279,6 +2660,15 @@ static int rk628_csi_probe(struct i2c_client *client, } v4l2_dbg(1, debug, sd, "CSITX VERSION: %#x\n", val); + if (rk628->version >= RK628F_VERSION) { + err = rk628_i2c_read(csi->rk628, CSITX1_CSITX_VERSION, &val); + if (err) { + v4l2_err(sd, "i2c access failed! err:%d\n", err); + return -ENODEV; + } + v4l2_dbg(1, debug, sd, "CSITX1 VERSION: %#x\n", val); + } + mutex_init(&csi->confctl_mutex); csi->txphy = rk628_txphy_register(rk628); @@ -2359,6 +2749,7 @@ static int rk628_csi_probe(struct i2c_client *client, rk628_csi_delayed_work_enable_hotplug); INIT_DELAYED_WORK(&csi->delayed_work_res_change, rk628_delayed_work_res_change); + INIT_WORK(&csi->work_isr, rk628_work_isr); csi->audio_info = rk628_hdmirx_audioinfo_alloc(dev, &csi->confctl_mutex, rk628, @@ -2412,6 +2803,8 @@ static int rk628_csi_probe(struct i2c_client *client, v4l2_err(sd, "v4l2 ctrl handler setup failed! err:%d\n", err); goto err_work_queues; } + csi->rk628->dual_mipi = false; + rk628_debugfs_create(csi->rk628); v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); @@ -2423,6 +2816,7 @@ err_work_queues: flush_work(&csi->work_i2c_poll); cancel_delayed_work(&csi->delayed_work_enable_hotplug); cancel_delayed_work(&csi->delayed_work_res_change); + cancel_work_sync(&csi->work_isr); rk628_hdmirx_audio_destroy(csi->audio_info); err_hdl: mutex_destroy(&csi->confctl_mutex); @@ -2431,10 +2825,15 @@ err_hdl: return err; } +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE static void rk628_csi_remove(struct i2c_client *client) +#else +static int rk628_csi_remove(struct i2c_client *client) +#endif { struct rk628_csi *csi = i2c_get_clientdata(client); + debugfs_remove_recursive(csi->rk628->debug_dir); if (!csi->hdmirx_irq) { del_timer_sync(&csi->timer); flush_work(&csi->work_i2c_poll); @@ -2443,6 +2842,7 @@ static void rk628_csi_remove(struct i2c_client *client) rk628_hdmirx_audio_cancel_work_rate_change(csi->audio_info, true); cancel_delayed_work_sync(&csi->delayed_work_enable_hotplug); cancel_delayed_work_sync(&csi->delayed_work_res_change); + cancel_work_sync(&csi->work_isr); if (csi->rxphy_pwron) rk628_rxphy_power_off(csi->rk628); @@ -2457,6 +2857,11 @@ static void rk628_csi_remove(struct i2c_client *client) rk628_control_assert(csi->rk628, RGU_CLK_RX); rk628_control_assert(csi->rk628, RGU_VOP); rk628_control_assert(csi->rk628, RGU_CSI); + if (csi->rk628->version >= RK628F_VERSION) + rk628_control_assert(csi->rk628, RGU_CSI1); +#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE + return 0; +#endif } static struct i2c_driver rk628_csi_i2c_driver = { diff --git a/drivers/media/i2c/rk628/rk628_dsi.c b/drivers/media/i2c/rk628/rk628_dsi.c index 71840bad821b..1712061fb992 100644 --- a/drivers/media/i2c/rk628/rk628_dsi.c +++ b/drivers/media/i2c/rk628/rk628_dsi.c @@ -58,7 +58,7 @@ static inline int dsi_update_bits(struct rk628 *rk628, int id, return rk628_i2c_update_bits(rk628, dsi_base + reg, mask, val); } -static void mipi_dphy_power_on_dsi(struct rk628_dsi *dsi) +static void mipi_dphy_power_on_dsi(struct rk628_dsi *dsi, uint8_t mipi_id) { int dev_id; unsigned int dsi_base; @@ -66,29 +66,38 @@ static void mipi_dphy_power_on_dsi(struct rk628_dsi *dsi) int ret; struct rk628 *rk628 = dsi->rk628; - dev_id = RK628_DEV_DSI0; - dsi_base = DSI0_BASE; + dev_id = mipi_id ? RK628_DEV_DSI1 : RK628_DEV_DSI0; + dsi_base = mipi_id ? DSI1_BASE : DSI0_BASE; - dsi_update_bits(rk628, 0, DSI_PHY_RSTZ, PHY_ENABLECLK, 0); - dsi_update_bits(rk628, 0, DSI_PHY_RSTZ, PHY_SHUTDOWNZ, 0); - dsi_update_bits(rk628, 0, DSI_PHY_RSTZ, PHY_RSTZ, 0); - testif_testclr_assert(dsi->rk628); + dsi_update_bits(rk628, mipi_id, DSI_PHY_RSTZ, PHY_ENABLECLK, 0); + dsi_update_bits(rk628, mipi_id, DSI_PHY_RSTZ, PHY_SHUTDOWNZ, 0); + dsi_update_bits(rk628, mipi_id, DSI_PHY_RSTZ, PHY_RSTZ, 0); + rk628_testif_testclr_assert(dsi->rk628, mipi_id); /* Set all REQUEST inputs to zero */ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, - FORCERXMODE_MASK | FORCETXSTOPMODE_MASK, - FORCETXSTOPMODE(0) | FORCERXMODE(0)); + if (mipi_id) + rk628_i2c_update_bits(rk628, GRF_MIPI_TX1_CON, + FORCERXMODE_MASK | FORCETXSTOPMODE_MASK, + FORCETXSTOPMODE(0) | FORCERXMODE(0)); + else + rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, + FORCERXMODE_MASK | FORCETXSTOPMODE_MASK, + FORCETXSTOPMODE(0) | FORCERXMODE(0)); udelay(1); - testif_testclr_deassert(dsi->rk628); + rk628_testif_testclr_deassert(dsi->rk628, mipi_id); + rk628_mipi_dphy_init_hsfreqrange(dsi->rk628, dsi->lane_mbps, mipi_id); - mipi_dphy_init_hsfreqrange(dsi->rk628, dsi->lane_mbps); + if (dsi->lane_mbps > 1100) + rk628_mipi_dphy_init_hsmanual(dsi->rk628, true, mipi_id); + else + rk628_mipi_dphy_init_hsmanual(dsi->rk628, false, mipi_id); - dsi_update_bits(rk628, 0, DSI_PHY_RSTZ, + dsi_update_bits(rk628, mipi_id, DSI_PHY_RSTZ, PHY_ENABLECLK, PHY_ENABLECLK); - dsi_update_bits(rk628, 0, DSI_PHY_RSTZ, + dsi_update_bits(rk628, mipi_id, DSI_PHY_RSTZ, PHY_SHUTDOWNZ, PHY_SHUTDOWNZ); - dsi_update_bits(rk628, 0, DSI_PHY_RSTZ, PHY_RSTZ, PHY_RSTZ); + dsi_update_bits(rk628, mipi_id, DSI_PHY_RSTZ, PHY_RSTZ, PHY_RSTZ); usleep_range(1500, 2000); rk628_txphy_power_on(rk628); @@ -112,42 +121,42 @@ static void mipi_dphy_power_on_dsi(struct rk628_dsi *dsi) udelay(10); } -static void rk628_dsi_pre_enable(struct rk628_dsi *dsi) +static void rk628_dsi_pre_enable(struct rk628_dsi *dsi, uint8_t mipi_id) { u32 val; struct rk628 *rk628 = dsi->rk628; u32 lane_mbps = dsi->lane_mbps; - dsi_write(rk628, 0, DSI_PWR_UP, RESET); - dsi_write(rk628, 0, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE)); + dsi_write(rk628, mipi_id, DSI_PWR_UP, RESET); + dsi_write(rk628, mipi_id, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE)); val = DIV_ROUND_UP(lane_mbps >> 3, 20); - dsi_write(rk628, 0, DSI_CLKMGR_CFG, + dsi_write(rk628, mipi_id, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) | TX_ESC_CLK_DIVISION(val)); val = CRC_RX_EN | ECC_RX_EN | BTA_EN | EOTP_TX_EN; if (dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET) val &= ~EOTP_TX_EN; - dsi_write(rk628, 0, DSI_PCKHDL_CFG, val); + dsi_write(rk628, mipi_id, DSI_PCKHDL_CFG, val); - dsi_write(rk628, 0, DSI_TO_CNT_CFG, + dsi_write(rk628, mipi_id, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000)); - dsi_write(rk628, 0, DSI_BTA_TO_CNT, 0xd00); - dsi_write(rk628, 0, DSI_PHY_TMR_CFG, + dsi_write(rk628, mipi_id, DSI_BTA_TO_CNT, 0xd00); + dsi_write(rk628, mipi_id, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x14) | PHY_LP2HS_TIME(0x10) | MAX_RD_TIME(10000)); - dsi_write(rk628, 0, DSI_PHY_TMR_LPCLK_CFG, + dsi_write(rk628, mipi_id, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40) | PHY_CLKLP2HS_TIME(0x40)); - dsi_write(rk628, 0, DSI_PHY_IF_CFG, + dsi_write(rk628, mipi_id, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) | N_LANES(4 - 1)); - mipi_dphy_power_on_dsi(dsi); + mipi_dphy_power_on_dsi(dsi, mipi_id); - dsi_write(rk628, 0, DSI_PWR_UP, POWER_UP); + dsi_write(rk628, mipi_id, DSI_PWR_UP, POWER_UP); } -static void rk628_dsi_set_vid_mode(struct rk628_dsi *dsi) +static void rk628_dsi_set_vid_mode(struct rk628_dsi *dsi, uint8_t mipi_id) { unsigned int lanebyteclk = (dsi->lane_mbps * 1000L) >> 3; u64 dpipclk; @@ -176,16 +185,16 @@ static void rk628_dsi_set_vid_mode(struct rk628_dsi *dsi) else val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS; - dsi_write(rk628, 0, DSI_VID_MODE_CFG, val); + dsi_write(rk628, mipi_id, DSI_VID_MODE_CFG, val); if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) - dsi_update_bits(rk628, 0, DSI_LPCLK_CTRL, + dsi_update_bits(rk628, mipi_id, DSI_LPCLK_CTRL, AUTO_CLKLANE_CTRL, AUTO_CLKLANE_CTRL); - pkt_size = VID_PKT_SIZE(bt->width); + pkt_size = rk628->dual_mipi ? VID_PKT_SIZE(bt->width) / 2 : VID_PKT_SIZE(bt->width); - dsi_write(rk628, 0, DSI_VID_PKT_SIZE, pkt_size); + dsi_write(rk628, mipi_id, DSI_VID_PKT_SIZE, pkt_size); vactive = bt->height; vs = bt->vsync; @@ -202,43 +211,47 @@ static void rk628_dsi_set_vid_mode(struct rk628_dsi *dsi) //hline_time = hline * lanebyteclk / dpipclk; hline_time = DIV_ROUND_CLOSEST_ULL(hline * lanebyteclk, dpipclk); - dsi_write(rk628, 0, DSI_VID_HLINE_TIME, + dsi_write(rk628, mipi_id, DSI_VID_HLINE_TIME, VID_HLINE_TIME(hline_time)); //hs_time = hs * lanebyteclk / dpipclk; hs_time = DIV_ROUND_CLOSEST_ULL(hs * lanebyteclk, dpipclk); - dsi_write(rk628, 0, DSI_VID_HSA_TIME, VID_HSA_TIME(hs_time)); + dsi_write(rk628, mipi_id, DSI_VID_HSA_TIME, VID_HSA_TIME(hs_time)); //hbp_time = hbp * lanebyteclk / dpipclk; hbp_time = DIV_ROUND_CLOSEST_ULL(hbp * lanebyteclk, dpipclk); - dsi_write(rk628, 0, DSI_VID_HBP_TIME, VID_HBP_TIME(hbp_time)); + dsi_write(rk628, mipi_id, DSI_VID_HBP_TIME, VID_HBP_TIME(hbp_time)); - dsi_write(rk628, 0, DSI_VID_VACTIVE_LINES, vactive); - dsi_write(rk628, 0, DSI_VID_VSA_LINES, vs); - dsi_write(rk628, 0, DSI_VID_VFP_LINES, vfp); - dsi_write(rk628, 0, DSI_VID_VBP_LINES, vbp); + dsi_write(rk628, mipi_id, DSI_VID_VACTIVE_LINES, vactive); + dsi_write(rk628, mipi_id, DSI_VID_VSA_LINES, vs); + dsi_write(rk628, mipi_id, DSI_VID_VFP_LINES, vfp); + dsi_write(rk628, mipi_id, DSI_VID_VBP_LINES, vbp); - dsi_write(rk628, 0, DSI_MODE_CFG, CMD_VIDEO_MODE(VIDEO_MODE)); + dsi_write(rk628, mipi_id, DSI_MODE_CFG, CMD_VIDEO_MODE(VIDEO_MODE)); } -static void rk628_dsi_set_cmd_mode(struct rk628_dsi *dsi) +static void rk628_dsi_set_cmd_mode(struct rk628_dsi *dsi, uint8_t mipi_id) { struct rk628 *rk628 = dsi->rk628; - dsi_update_bits(rk628, 0, DSI_CMD_MODE_CFG, DCS_LW_TX, 0); - dsi_write(rk628, 0, DSI_EDPI_CMD_SIZE, - EDPI_ALLOWED_CMD_SIZE(dsi->timings.bt.width)); - dsi_write(rk628, 0, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE)); + dsi_update_bits(rk628, mipi_id, DSI_CMD_MODE_CFG, DCS_LW_TX, 0); + if (rk628->dual_mipi) + dsi_write(rk628, mipi_id, DSI_EDPI_CMD_SIZE, + EDPI_ALLOWED_CMD_SIZE(dsi->timings.bt.width / 2)); + else + dsi_write(rk628, mipi_id, DSI_EDPI_CMD_SIZE, + EDPI_ALLOWED_CMD_SIZE(dsi->timings.bt.width)); + dsi_write(rk628, mipi_id, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE)); } -static void rk628_dsi_enable(struct rk628_dsi *dsi) +static void rk628_dsi_enable(struct rk628_dsi *dsi, uint8_t mipi_id) { u32 val; struct rk628 *rk628 = dsi->rk628; - dsi_write(rk628, 0, DSI_PWR_UP, RESET); + dsi_write(rk628, mipi_id, DSI_PWR_UP, RESET); val = DPI_COLOR_CODING(5); - dsi_write(rk628, 0, DSI_DPI_COLOR_CODING, val); + dsi_write(rk628, mipi_id, DSI_DPI_COLOR_CODING, val); val = 0; @@ -249,26 +262,27 @@ static void rk628_dsi_enable(struct rk628_dsi *dsi) * val |= HSYNC_ACTIVE_LOW; */ - dsi_write(rk628, 0, DSI_DPI_CFG_POL, val); + dsi_write(rk628, mipi_id, DSI_DPI_CFG_POL, val); - dsi_write(rk628, 0, DSI_DPI_VCID, DPI_VID(0)); - dsi_write(rk628, 0, DSI_DPI_LP_CMD_TIM, + dsi_write(rk628, mipi_id, DSI_DPI_VCID, DPI_VID(0)); + dsi_write(rk628, mipi_id, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4) | INVACT_LPCMD_TIME(4)); - dsi_update_bits(rk628, 0, DSI_LPCLK_CTRL, - PHY_TXREQUESTCLKHS | AUTO_CLKLANE_CTRL, - PHY_TXREQUESTCLKHS | AUTO_CLKLANE_CTRL); + dsi_update_bits(rk628, mipi_id, DSI_LPCLK_CTRL, + PHY_TXREQUESTCLKHS, + PHY_TXREQUESTCLKHS); if (dsi->vid_mode == VIDEO_MODE) - rk628_dsi_set_vid_mode(dsi); + rk628_dsi_set_vid_mode(dsi, mipi_id); else - rk628_dsi_set_cmd_mode(dsi); + rk628_dsi_set_cmd_mode(dsi, mipi_id); - dsi_write(rk628, 0, DSI_PWR_UP, POWER_UP); + dsi_write(rk628, mipi_id, DSI_PWR_UP, POWER_UP); } -static u32 rk628_dsi_get_lane_rate(struct rk628_dsi *dsi) +u32 rk628_dsi_get_lane_rate_mbps(struct rk628_dsi *dsi) { + struct rk628 *rk628 = dsi->rk628; u32 lane_rate; - u32 max_lane_rate = 1500; + u32 max_lane_rate = 1800; u8 bpp, lanes; u64 pixelclock = dsi->timings.bt.pixelclock; @@ -278,33 +292,81 @@ static u32 rk628_dsi_get_lane_rate(struct rk628_dsi *dsi) lane_rate = pixelclock * bpp; lane_rate = div_u64(lane_rate, lanes); lane_rate = DIV_ROUND_UP(lane_rate * 5, 4); + if (rk628->dual_mipi) + lane_rate /= 2; - if (lane_rate > max_lane_rate) + if (lane_rate > 1300) lane_rate = max_lane_rate; + else if (lane_rate > 700 && lane_rate <= 1300) + lane_rate = 1300; + else + lane_rate = 700; return lane_rate; } +EXPORT_SYMBOL(rk628_dsi_get_lane_rate_mbps); void rk628_mipi_dsi_power_on(struct rk628_dsi *dsi) { struct rk628 *rk628 = dsi->rk628; - u32 rate = rk628_dsi_get_lane_rate(dsi); + u32 rate = rk628_dsi_get_lane_rate_mbps(dsi); int bus_width; + u32 mask = SW_OUTPUT_MODE_MASK; + u32 val = SW_OUTPUT_MODE(OUTPUT_MODE_DSI); - rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_OUTPUT_MODE_MASK, - SW_OUTPUT_MODE(OUTPUT_MODE_DSI)); - rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, SW_SPLIT_EN, 0); + if (rk628->version == RK628F_VERSION) { + mask = SW_OUTPUT_COMBTX_MODE_MASK; + val = SW_OUTPUT_COMBTX_MODE(OUTPUT_MODE_DSI - 2); + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON3, + GRF_AS_DSIPHY_MASK, + GRF_AS_DSIPHY(1)); + } + + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, mask, val); + + dev_info(dsi->rk628->dev, "%s mipi mode, %sable dphy1 and split en\n", + rk628->dual_mipi ? "dual" : "single", rk628->dual_mipi ? "en" : "dis"); + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON3, GRF_DPHY_CH1_EN_MASK, + rk628->dual_mipi ? GRF_DPHY_CH1_EN(1) : GRF_DPHY_CH1_EN(0)); + rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, SW_SPLIT_EN, + rk628->dual_mipi ? 1 : 0); bus_width = rate << 8; bus_width |= COMBTXPHY_MODULEA_EN; + if (rk628->dual_mipi) + bus_width |= COMBTXPHY_MODULEB_EN; rk628_txphy_set_bus_width(dsi->rk628, bus_width); rk628_txphy_set_mode(dsi->rk628, PHY_MODE_VIDEO_MIPI); dsi->lane_mbps = rk628_txphy_get_bus_width(dsi->rk628); - dev_dbg(dsi->rk628->dev, "%s mipi bitrate:%llu mbps\n", __func__, + dev_info(dsi->rk628->dev, "%s mipi bitrate:%llu mbps\n", __func__, dsi->lane_mbps); - rk628_dsi_pre_enable(dsi); + rk628_dsi_pre_enable(dsi, 0); + if (rk628->dual_mipi) + rk628_dsi_pre_enable(dsi, 1); - rk628_dsi_enable(dsi); + rk628_dsi_enable(dsi, 0); + if (rk628->dual_mipi) + rk628_dsi_enable(dsi, 1); } EXPORT_SYMBOL(rk628_mipi_dsi_power_on); + +void rk628_dsi_disable_stream(struct rk628_dsi *dsi) +{ + struct rk628 *rk628 = dsi->rk628; + + dsi_write(rk628, 0, DSI_PWR_UP, RESET); + dsi_write(rk628, 0, DSI_LPCLK_CTRL, 0); + dsi_write(rk628, 0, DSI_EDPI_CMD_SIZE, 0); + dsi_write(rk628, 0, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE)); + dsi_write(rk628, 0, DSI_PWR_UP, POWER_UP); + + dsi_write(rk628, 1, DSI_PWR_UP, RESET); + dsi_write(rk628, 1, DSI_LPCLK_CTRL, 0); + dsi_write(rk628, 1, DSI_EDPI_CMD_SIZE, 0); + dsi_write(rk628, 1, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE)); + dsi_write(rk628, 1, DSI_PWR_UP, POWER_UP); + + rk628_txphy_power_off(rk628); +} +EXPORT_SYMBOL(rk628_dsi_disable_stream); diff --git a/drivers/media/i2c/rk628/rk628_dsi.h b/drivers/media/i2c/rk628/rk628_dsi.h index ad46aa40b87d..1c6e623c6568 100644 --- a/drivers/media/i2c/rk628/rk628_dsi.h +++ b/drivers/media/i2c/rk628/rk628_dsi.h @@ -166,8 +166,11 @@ struct rk628_dsi { u64 lane_mbps; int vid_mode; int mode_flags; + uint8_t id; }; void rk628_mipi_dsi_power_on(struct rk628_dsi *dsi); +void rk628_dsi_disable_stream(struct rk628_dsi *dsi); +u32 rk628_dsi_get_lane_rate_mbps(struct rk628_dsi *dsi); #endif diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.c b/drivers/media/i2c/rk628/rk628_hdmirx.c index b59378fed27f..cf64ed580639 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.c +++ b/drivers/media/i2c/rk628/rk628_hdmirx.c @@ -18,15 +18,6 @@ #define INIT_FIFO_STATE 64 -#define is_validfs(x) (x == 32000 || \ - x == 44100 || \ - x == 48000 || \ - x == 88200 || \ - x == 96000 || \ - x == 176400 || \ - x == 192000 || \ - x == 768000) - struct rk628_audiostate { u32 hdmirx_aud_clkrate; u32 fs_audio; @@ -53,6 +44,25 @@ struct rk628_audioinfo { struct device *dev; }; +struct hdmirx_tmdsclk_cnt { + u32 tmds_cnt; + u8 cnt; +}; + +#define HDMIRX_GET_TMDSCLK_TIME 21 + +static int supported_fs[] = { + 32000, + 44100, + 48000, + 88200, + 96000, + 176400, + 192000, + 768000, + -1 +}; + static int hdcp_load_keys_cb(struct rk628 *rk628, struct rk628_hdcp *hdcp) { int size; @@ -168,11 +178,11 @@ EXPORT_SYMBOL(rk628_hdmirx_set_hdcp); void rk628_hdmirx_controller_setup(struct rk628 *rk628) { - rk628_i2c_write(rk628, HDMI_RX_HDMI20_CONTROL, 0x10000f10); + rk628_i2c_write(rk628, HDMI_RX_HDMI20_CONTROL, 0x10000f11); rk628_i2c_write(rk628, HDMI_RX_HDMI_MODE_RECOVER, 0x00000021); rk628_i2c_write(rk628, HDMI_RX_PDEC_CTRL, 0xbfff8011); rk628_i2c_write(rk628, HDMI_RX_PDEC_ASP_CTRL, 0x00000040); - rk628_i2c_write(rk628, HDMI_RX_HDMI_RESMPL_CTRL, 0x00000001); + rk628_i2c_write(rk628, HDMI_RX_HDMI_RESMPL_CTRL, 0x00000000); rk628_i2c_write(rk628, HDMI_RX_HDMI_SYNC_CTRL, 0x00000014); rk628_i2c_write(rk628, HDMI_RX_PDEC_ERR_FILTER, 0x00000008); rk628_i2c_write(rk628, HDMI_RX_SCDC_I2CCONFIG, 0x01000000); @@ -181,7 +191,7 @@ void rk628_hdmirx_controller_setup(struct rk628 *rk628) rk628_i2c_write(rk628, HDMI_RX_CHLOCK_CONFIG, 0x0030c15c); rk628_i2c_write(rk628, HDMI_RX_HDMI_ERROR_PROTECT, 0x000d0c98); rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL1, 0x00000010); - rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL2, 0x00001738); + rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL2, 0x0000173a); rk628_i2c_write(rk628, HDMI_RX_MD_VCTRL, 0x00000002); rk628_i2c_write(rk628, HDMI_RX_MD_VTH, 0x0000073a); rk628_i2c_write(rk628, HDMI_RX_MD_IL_POL, 0x00000004); @@ -189,7 +199,7 @@ void rk628_hdmirx_controller_setup(struct rk628 *rk628) rk628_i2c_write(rk628, HDMI_RX_HDMI_DCM_CTRL, 0x00040414); rk628_i2c_write(rk628, HDMI_RX_HDMI_CKM_EVLTM, 0x00103e70); rk628_i2c_write(rk628, HDMI_RX_HDMI_CKM_F, 0x0c1c0b54); - rk628_i2c_write(rk628, HDMI_RX_HDMI_RESMPL_CTRL, 0x00000001); + rk628_i2c_update_bits(rk628, HDMI_RX_HDMI_TIMER_CTRL, VIDEO_PERIOD_MASK, VIDEO_PERIOD(1)); rk628_i2c_update_bits(rk628, HDMI_RX_HDCP_SETTINGS, HDMI_RESERVED_MASK | @@ -203,6 +213,39 @@ void rk628_hdmirx_controller_setup(struct rk628 *rk628) } EXPORT_SYMBOL(rk628_hdmirx_controller_setup); +static bool is_validfs(int fs) +{ + int i = 0; + int fs_t; + + fs_t = supported_fs[i++]; + while (fs_t > 0) { + if (fs == fs_t) + return true; + fs_t = supported_fs[i++]; + }; + return false; +} + +static int rk628_hdmirx_audio_find_closest_fs(struct rk628_audioinfo *aif, int fs) +{ + int i = 0; + int fs_t; + int difference; + + fs_t = supported_fs[i++]; + while (fs_t > 0) { + difference = abs(fs - fs_t); + if (difference <= 2000) { + if (fs != fs_t) + dev_dbg(aif->dev, "%s fix fs from %u to %u", __func__, fs, fs_t); + return fs_t; + } + fs_t = supported_fs[i++]; + }; + return fs_t; +} + static void rk628_hdmirx_audio_fifo_init(struct rk628_audioinfo *aif) { @@ -246,8 +289,7 @@ static u32 _rk628_hdmirx_audio_fs(struct rk628_audioinfo *aif) if (cts_decoded != 0) { fs_audio = div_u64((tmdsclk * n_decoded), cts_decoded); fs_audio /= 128; - fs_audio = div_u64(fs_audio + 50, 100); - fs_audio *= 100; + fs_audio = rk628_hdmirx_audio_find_closest_fs(aif, fs_audio); } dev_dbg(aif->dev, "%s: clkrate:%u tmdsclk:%llu, n_decoded:%u, cts_decoded:%u, fs_audio:%u\n", @@ -306,6 +348,85 @@ static void rk628_hdmirx_audio_enable(struct rk628_audioinfo *aif) AFIF_OVERFL_ISTS | AFIF_UNDERFL_ISTS); } +static const char *audio_fifo_err(u32 fifo_status) +{ + switch (fifo_status & (AFIF_UNDERFL_ISTS | AFIF_OVERFL_ISTS)) { + case AFIF_UNDERFL_ISTS: + return "underflow"; + case AFIF_OVERFL_ISTS: + return "overflow"; + case AFIF_UNDERFL_ISTS | AFIF_OVERFL_ISTS: + return "underflow and overflow"; + } + return "underflow or overflow"; +} + +static void rk628_csi_delayed_work_audio_v2(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct rk628_audioinfo *aif = container_of(dwork, struct rk628_audioinfo, + delayed_work_audio); + struct rk628_audiostate *audio_state = &aif->audio_state; + struct rk628 *rk628 = aif->rk628; + u32 fs_audio; + int init_state, pre_state, fifo_status, fifo_ints; + unsigned long delay = 500; + + fs_audio = _rk628_hdmirx_audio_fs(aif); + /* read fifo init status */ + rk628_i2c_read(rk628, HDMI_RX_AUD_FIFO_ISTS, &fifo_ints); + dev_dbg(rk628->dev, "%s: HDMI_RX_AUD_FIFO_ISTS:%#x\r\n", __func__, fifo_ints); + + if (fifo_ints & (AFIF_UNDERFL_ISTS | AFIF_OVERFL_ISTS)) { + dev_warn(rk628->dev, "%s: audio %s %#x, with fs %svalid %d\n", + __func__, audio_fifo_err(fifo_ints), fifo_ints, + is_validfs(fs_audio) ? "" : "in", fs_audio); + if (is_validfs(fs_audio)) + rk628_hdmirx_audio_set_fs(aif, fs_audio); + rk628_hdmirx_audio_fifo_init(aif); + audio_state->pre_state = 0; + goto exit; + } + + /* read fifo fill status */ + init_state = audio_state->init_state; + pre_state = audio_state->pre_state; + rk628_i2c_read(rk628, HDMI_RX_AUD_FIFO_FILLSTS1, &fifo_status); + dev_dbg(rk628->dev, + "%s: HDMI_RX_AUD_FIFO_FILLSTS1:%#x, single offset:%d, total offset:%d\n", + __func__, fifo_status, fifo_status - pre_state, fifo_status - init_state); + if (!is_validfs(fs_audio)) { + dev_dbg(rk628->dev, "%s: no supported fs(%u), fifo_status %d\n", + __func__, fs_audio, fifo_status); + delay = 1000; + } else if (abs(fs_audio - audio_state->fs_audio) > 1000) { + dev_info(rk628->dev, "%s: restart audio fs(%d -> %d)\n", + __func__, audio_state->fs_audio, fs_audio); + rk628_hdmirx_audio_set_fs(aif, fs_audio); + rk628_hdmirx_audio_fifo_init(aif); + audio_state->pre_state = 0; + goto exit; + } + if (fifo_status != 0) { + if (!aif->audio_present) { + dev_info(rk628->dev, "audio on"); + aif->audio_present = true; + } + if (fifo_status - init_state > 16 && fifo_status - pre_state > 0) + rk628_hdmirx_audio_clk_inc_rate(aif, 10); + else if (fifo_status - init_state < -16 && fifo_status - pre_state < 0) + rk628_hdmirx_audio_clk_inc_rate(aif, -10); + } else { + if (aif->audio_present) { + dev_info(rk628->dev, "audio off"); + aif->audio_present = false; + } + } + audio_state->pre_state = fifo_status; +exit: + schedule_delayed_work(&aif->delayed_work_audio, msecs_to_jiffies(delay)); +} + static void rk628_csi_delayed_work_audio(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); @@ -395,10 +516,13 @@ HAUDINFO rk628_hdmirx_audioinfo_alloc(struct device *dev, aif = devm_kzalloc(dev, sizeof(*aif), GFP_KERNEL); if (!aif) return NULL; - INIT_DELAYED_WORK(&aif->delayed_work_audio_rate_change, - rk628_csi_delayed_work_audio_rate_change); - INIT_DELAYED_WORK(&aif->delayed_work_audio, - rk628_csi_delayed_work_audio); + if (rk628->version >= RK628F_VERSION) { + INIT_DELAYED_WORK(&aif->delayed_work_audio, rk628_csi_delayed_work_audio_v2); + } else { + INIT_DELAYED_WORK(&aif->delayed_work_audio, rk628_csi_delayed_work_audio); + INIT_DELAYED_WORK(&aif->delayed_work_audio_rate_change, + rk628_csi_delayed_work_audio_rate_change); + } aif->confctl_mutex = confctl_mutex; aif->rk628 = rk628; aif->i2s_enabled_default = en; @@ -432,11 +556,14 @@ EXPORT_SYMBOL(rk628_hdmirx_audio_cancel_work_rate_change); void rk628_hdmirx_audio_destroy(HAUDINFO info) { struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info; + struct rk628 *rk628; if (!aif) return; + rk628 = aif->rk628; rk628_hdmirx_audio_cancel_work_audio(aif, true); - rk628_hdmirx_audio_cancel_work_rate_change(aif, true); + if (rk628->version < RK628F_VERSION) + rk628_hdmirx_audio_cancel_work_rate_change(aif, true); aif->confctl_mutex = NULL; aif->rk628 = NULL; } @@ -485,6 +612,7 @@ EXPORT_SYMBOL(rk628_hdmirx_audio_i2s_ctrl); void rk628_hdmirx_audio_setup(HAUDINFO info) { struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info; + struct rk628 *rk628 = aif->rk628; u32 audio_pll_n, audio_pll_cts; dev_dbg(aif->dev, "%s: setup audio\n", __func__); @@ -500,6 +628,9 @@ void rk628_hdmirx_audio_setup(HAUDINFO info) aif->ctsn_ints_en = false; aif->i2s_enabled = false; + if (rk628->version >= RK628F_VERSION) + rk628_i2c_write(rk628, CRU_MODE_CON00, HIWORD_UPDATE(1, 4, 4)); + rk628_hdmirx_audio_clk_set_rate(aif, 5644800); /* manual aud CTS */ rk628_i2c_write(aif->rk628, HDMI_RX_AUDPLL_GEN_CTS, audio_pll_cts); @@ -544,11 +675,17 @@ void rk628_hdmirx_audio_setup(HAUDINFO info) PAO_RATE(0)); rk628_i2c_write(aif->rk628, HDMI_RX_AUD_CHEXTR_CTRL, AUD_LAYOUT_CTRL(1)); - aif->ctsn_ints_en = true; - rk628_i2c_write(aif->rk628, HDMI_RX_PDEC_IEN_SET, ACR_N_CHG_ICLR | ACR_CTS_CHG_ICLR); - /* audio detect */ - rk628_i2c_write(aif->rk628, HDMI_RX_PDEC_AUDIODET_CTRL, - AUDIODET_THRESHOLD(0)); + if (rk628->version >= RK628F_VERSION) { + rk628_i2c_update_bits(aif->rk628, HDMI_RX_DMI_DISABLE_IF, + AUD_ENABLE_MASK, AUD_ENABLE(1)); + schedule_delayed_work(&aif->delayed_work_audio, msecs_to_jiffies(1000)); + } else { + aif->ctsn_ints_en = true; + rk628_i2c_write(aif->rk628, HDMI_RX_PDEC_IEN_SET, + ACR_N_CHG_ICLR | ACR_CTS_CHG_ICLR); + /* audio detect */ + rk628_i2c_write(aif->rk628, HDMI_RX_PDEC_AUDIODET_CTRL, AUDIODET_THRESHOLD(0)); + } } EXPORT_SYMBOL(rk628_hdmirx_audio_setup); @@ -616,6 +753,9 @@ int rk628_is_avi_ready(struct rk628 *rk628, bool avi_rcv_rdy) u8 cnt = 0, max_cnt = 2; u32 hdcp_ctrl_val = 0; + if (rk628->version >= RK628F_VERSION) + return 1; + rk628_i2c_read(rk628, HDMI_RX_HDCP_CTRL, &val); if ((val & HDCP_ENABLE_MASK)) max_cnt = 5; @@ -646,3 +786,430 @@ int rk628_is_avi_ready(struct rk628 *rk628, bool avi_rcv_rdy) return 1; } EXPORT_SYMBOL(rk628_is_avi_ready); + +static void hdmirxphy_write(struct rk628 *rk628, u32 offset, u32 val) +{ + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_ADDRESS, offset); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_DATAO, val); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_OPERATION, 1); +} + +static __maybe_unused u32 hdmirxphy_read(struct rk628 *rk628, u32 offset) +{ + u32 val; + + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_ADDRESS, offset); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_OPERATION, 2); + rk628_i2c_read(rk628, HDMI_RX_I2CM_PHYG3_DATAI, &val); + + return val; +} + +static void rk628_hdmirxphy_enable(struct rk628 *rk628, bool is_hdmi2, bool scramble_en) +{ + hdmirxphy_write(rk628, 0x02, 0x1860); + hdmirxphy_write(rk628, 0x03, 0x0060); + if (!is_hdmi2 && scramble_en) + hdmirxphy_write(rk628, 0x0d, 0x00c0); + else + hdmirxphy_write(rk628, 0x0d, 0x0); + hdmirxphy_write(rk628, 0x27, 0x1c94); + hdmirxphy_write(rk628, 0x28, 0x3713); + hdmirxphy_write(rk628, 0x29, 0x24da); + hdmirxphy_write(rk628, 0x2a, 0x5492); + hdmirxphy_write(rk628, 0x2b, 0x4b0d); + hdmirxphy_write(rk628, 0x2d, 0x008c); + hdmirxphy_write(rk628, 0x2e, 0x0001); + + if (is_hdmi2) + hdmirxphy_write(rk628, 0x0e, 0x0108); + else + hdmirxphy_write(rk628, 0x0e, 0x0008); + +} + +static void rk628_hdmirxphy_set_clrdpt(struct rk628 *rk628, bool is_8bit) +{ + if (is_8bit) + hdmirxphy_write(rk628, 0x03, 0x0000); + else + hdmirxphy_write(rk628, 0x03, 0x0060); +} + +void rk628_hdmirx_verisyno_phy_power_on(struct rk628 *rk628) +{ + bool is_hdmi2 = false; + u32 val; + int i; + bool scramble = false; + + /* wait tx to write scdc tmds ratio */ + for (i = 0; i < 50; i++) { + rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS0, &val); + if (val & SCDC_TMDSBITCLKRATIO) + break; + msleep(20); + } + + if (val & SCDC_TMDSBITCLKRATIO) + is_hdmi2 = true; + + rk628_i2c_read(rk628, HDMI_RX_HDMI20_STATUS, &val); + scramble = (val & SCRAMBDET_MASK) ? true : false; + + dev_info(rk628->dev, "%s: %s, %s\n", __func__, is_hdmi2 ? "hdmi2.0" : "hdmi1.4", + scramble ? "Scramble" : "Descramble"); + /* power down phy */ + rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x17); + usleep_range(20, 30); + rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x15); + /* init phy i2c */ + rk628_i2c_write(rk628, HDMI_RX_SNPS_PHYG3_CTRL, 0); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_SS_CNTS, 0x018c01d2); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_FS_HCNT, 0x003c0081); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_MODE, 1); + rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x11); + /* enable rx phy */ + rk628_hdmirxphy_enable(rk628, is_hdmi2, scramble); + rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x14); + msleep(20); +} +EXPORT_SYMBOL(rk628_hdmirx_verisyno_phy_power_on); + +void rk628_hdmirx_phy_prepclk_cfg(struct rk628 *rk628) +{ + u32 format; + bool is_clrdpt_8bit = false; + + usleep_range(20 * 1000, 30 * 1000); + rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &format); + format = (format & VIDEO_FORMAT_MASK) >> 5; + dev_info(rk628->dev, "%s: format = %d from AVI\n", __func__, format); + + /* yuv420 should set phy color depth 8bit */ + if (format == 3) + is_clrdpt_8bit = true; + + rk628_i2c_read(rk628, HDMI_RX_PDEC_GCP_AVMUTE, &format); + format = (format & PKTDEC_GCP_CD_MASK) >> 4; + dev_info(rk628->dev, "%s: format = %d from GCP\n", __func__, format); + + /* 10bit color depth should set phy color depth 8bit */ + if (format == 5) + is_clrdpt_8bit = true; + + rk628_hdmirxphy_set_clrdpt(rk628, is_clrdpt_8bit); +} +EXPORT_SYMBOL(rk628_hdmirx_phy_prepclk_cfg); + +static const char * const bus_format_str[] = { + "RGB", + "YUV422", + "YUV444", + "YUV420", + "UNKNOWN", +}; + +u8 rk628_hdmirx_get_format(struct rk628 *rk628) +{ + u32 val; + u8 video_fmt; + + rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &val); + video_fmt = (val & VIDEO_FORMAT_MASK) >> 5; + if (video_fmt > BUS_FMT_UNKNOWN) + video_fmt = BUS_FMT_UNKNOWN; + dev_info(rk628->dev, "%s: format = %s\n", __func__, bus_format_str[video_fmt]); + + /* + * set avmute value to black + * RGB: R: CH2[15:0], G:CH0_1[31:16], B: CH0_1[15:0] + * YUV: Cr:CH2[15:0], Y:CH0_1[31:16], Cb:CH0_1[15:0] + */ + if (video_fmt == BUS_FMT_RGB) { + rk628_i2c_write(rk628, HDMI_VM_CFG_CH0_1, 0x0); + rk628_i2c_write(rk628, HDMI_VM_CFG_CH2, 0x0); + } else { + rk628_i2c_write(rk628, HDMI_VM_CFG_CH0_1, 0x00008000); + rk628_i2c_write(rk628, HDMI_VM_CFG_CH2, 0x8000); + } + + return video_fmt; +} +EXPORT_SYMBOL(rk628_hdmirx_get_format); + +void rk628_set_bg_enable(struct rk628 *rk628, bool en) +{ + if (en) { + if (rk628->tx_mode) + rk628_i2c_write(rk628, GRF_BG_CTRL, + BG_R_OR_V(0) | BG_B_OR_U(0) | BG_G_OR_Y(0) | BG_ENABLE(1)); + else + rk628_i2c_write(rk628, GRF_BG_CTRL, + BG_R_OR_V(512) | BG_B_OR_U(512) | BG_G_OR_Y(64) | BG_ENABLE(1)); + return; + } + rk628_i2c_write(rk628, GRF_BG_CTRL, BG_ENABLE(0)); +} +EXPORT_SYMBOL(rk628_set_bg_enable); + +u32 rk628_hdmirx_get_tmdsclk_cnt(struct rk628 *rk628) +{ + int i, j; + u32 val, tmdsclk_cnt = 0; + struct hdmirx_tmdsclk_cnt tmdsclk[HDMIRX_GET_TMDSCLK_TIME] = {0}; + + for (i = 0; i < HDMIRX_GET_TMDSCLK_TIME; i++) { + rk628_i2c_read(rk628, HDMI_RX_HDMI_CKM_RESULT, &val); + tmdsclk_cnt = val & 0xffff; + for (j = 0; j < HDMIRX_GET_TMDSCLK_TIME; j++) { + if (tmdsclk_cnt == tmdsclk[j].tmds_cnt || !tmdsclk[j].tmds_cnt) { + tmdsclk[j].tmds_cnt = tmdsclk_cnt; + tmdsclk[j].cnt++; + break; + } + } + } + + for (i = 0; i < HDMIRX_GET_TMDSCLK_TIME; i++) { + if (!tmdsclk[i].tmds_cnt) + return tmdsclk_cnt; + + dev_info(rk628->dev, "tmdsclk_cnt: %d, cnt: %d\n", + tmdsclk[i].tmds_cnt, tmdsclk[i].cnt); + if (!i) + tmdsclk_cnt = tmdsclk[i].tmds_cnt; + else if (tmdsclk[i].cnt > tmdsclk[i - 1].cnt) + tmdsclk_cnt = tmdsclk[i].tmds_cnt; + } + + return tmdsclk_cnt; +} +EXPORT_SYMBOL(rk628_hdmirx_get_tmdsclk_cnt); + +static int rk628_hdmirx_read_timing(struct rk628 *rk628, + struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + u32 hact, vact, htotal, vtotal, fps, status; + u32 val; + u32 modetclk_cnt_hs, modetclk_cnt_vs, hs, vs; + u32 hofs_pix, hbp, hfp, vbp, vfp; + u32 tmds_clk, tmdsclk_cnt; + u64 tmp_data; + u8 video_fmt; + + memset(timings, 0, sizeof(struct v4l2_dv_timings)); + timings->type = V4L2_DV_BT_656_1120; + rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS1, &val); + status = val; + + rk628_i2c_read(rk628, HDMI_RX_MD_STS, &val); + bt->interlaced = val & ILACE_STS ? + V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; + + rk628_i2c_read(rk628, HDMI_RX_MD_HACT_PX, &val); + hact = val & 0xffff; + rk628_i2c_read(rk628, HDMI_RX_MD_VAL, &val); + vact = val & 0xffff; + rk628_i2c_read(rk628, HDMI_RX_MD_HT1, &val); + htotal = (val >> 16) & 0xffff; + rk628_i2c_read(rk628, HDMI_RX_MD_VTL, &val); + vtotal = val & 0xffff; + rk628_i2c_read(rk628, HDMI_RX_MD_HT1, &val); + hofs_pix = val & 0xffff; + rk628_i2c_read(rk628, HDMI_RX_MD_VOL, &val); + vbp = (val & 0xffff) + 1; + + tmdsclk_cnt = rk628_hdmirx_get_tmdsclk_cnt(rk628); + tmp_data = tmdsclk_cnt; + tmp_data = ((tmp_data * HDMIRX_MODETCLK_HZ) + HDMIRX_MODETCLK_CNT_NUM / 2); + do_div(tmp_data, HDMIRX_MODETCLK_CNT_NUM); + tmds_clk = tmp_data; + if (!htotal || !vtotal || bt->interlaced || vtotal > 3000) { + dev_err(rk628->dev, "timing err, %s htotal:%d, vtotal:%d\n", + bt->interlaced ? "interlaced is not supported," : "", + htotal, vtotal); + goto TIMING_ERR; + } + if (rk628->version >= RK628F_VERSION) + fps = tmds_clk / (htotal * vtotal); + else + fps = (tmds_clk + (htotal * vtotal) / 2) / (htotal * vtotal); + + rk628_i2c_read(rk628, HDMI_RX_MD_HT0, &val); + modetclk_cnt_hs = val & 0xffff; + hs = (tmdsclk_cnt * modetclk_cnt_hs + HDMIRX_MODETCLK_CNT_NUM / 2) / + HDMIRX_MODETCLK_CNT_NUM; + + rk628_i2c_read(rk628, HDMI_RX_MD_VSC, &val); + modetclk_cnt_vs = val & 0xffff; + vs = (tmdsclk_cnt * modetclk_cnt_vs + HDMIRX_MODETCLK_CNT_NUM / 2) / + HDMIRX_MODETCLK_CNT_NUM; + vs = (vs + htotal / 2) / htotal; + + if ((hofs_pix < hs) || (htotal < (hact + hofs_pix)) || + (vtotal < (vact + vs + vbp)) || !vs) { + dev_err(rk628->dev, "timing err, total:%dx%d, act:%dx%d, hofs:%d, hs:%d, vs:%d, vbp:%d\n", + htotal, vtotal, hact, vact, hofs_pix, hs, vs, vbp); + goto TIMING_ERR; + } + hbp = hofs_pix - hs; + hfp = htotal - hact - hofs_pix; + vfp = vtotal - vact - vs - vbp; + + video_fmt = rk628_hdmirx_get_format(rk628); + if (video_fmt == BUS_FMT_YUV420) { + htotal *= 2; + hact *= 2; + hfp *= 2; + hbp *= 2; + hs *= 2; + } + + dev_info(rk628->dev, "cnt_num:%d, tmds_cnt:%d, hs_cnt:%d, vs_cnt:%d, hofs:%d\n", + HDMIRX_MODETCLK_CNT_NUM, tmdsclk_cnt, modetclk_cnt_hs, modetclk_cnt_vs, hofs_pix); + + bt->width = hact; + bt->height = vact; + bt->hfrontporch = hfp; + bt->hsync = hs; + bt->hbackporch = hbp; + bt->vfrontporch = vfp; + bt->vsync = vs; + bt->vbackporch = vbp; + if (rk628->version >= RK628F_VERSION) + bt->pixelclock = tmds_clk; + else + bt->pixelclock = htotal * vtotal * fps; + + if (bt->interlaced == V4L2_DV_INTERLACED) { + bt->height *= 2; + bt->il_vsync = bt->vsync + 1; + bt->pixelclock /= 2; + } + if (video_fmt == BUS_FMT_YUV420) + bt->pixelclock *= 2; + + if (vact == 1080 && vtotal > 1500) + goto TIMING_ERR; + + dev_info(rk628->dev, "SCDC_REGS1:%#x, act:%dx%d, total:%dx%d, fps:%d, pixclk:%llu\n", + status, hact, vact, htotal, vtotal, fps, bt->pixelclock); + + return 0; + +TIMING_ERR: + return -ENOLCK; +} + +static int rk628_hdmirx_try_to_get_timing(struct rk628 *rk628, + struct v4l2_dv_timings *timings) +{ + int ret, i; + + for (i = 0; i < 5; i++) { + ret = rk628_hdmirx_read_timing(rk628, timings); + if (!ret) + return ret; + msleep(20); + } + + return ret; +} + +int rk628_hdmirx_get_timings(struct rk628 *rk628, + struct v4l2_dv_timings *timings) +{ + int i, cnt = 0, ret = 0; + u32 last_w, last_h; + u8 last_fmt; + struct v4l2_bt_timings *bt = &timings->bt; + + last_w = 0; + last_h = 0; + last_fmt = BUS_FMT_RGB; + + for (i = 0; i < HDMIRX_GET_TIMING_CNT; i++) { + ret = rk628_hdmirx_try_to_get_timing(rk628, timings); + if ((last_w == 0) && (last_h == 0)) { + last_w = bt->width; + last_h = bt->height; + last_fmt = rk628_hdmirx_get_format(rk628); + } + + if (ret || (last_w != bt->width) || (last_h != bt->height) + || (last_fmt != rk628_hdmirx_get_format(rk628))) + cnt = 0; + else + cnt++; + + if (cnt >= 8) + break; + + last_w = bt->width; + last_h = bt->height; + last_fmt = rk628_hdmirx_get_format(rk628); + usleep_range(10*1000, 10*1100); + } + + if (cnt < 8) { + dev_info(rk628->dev, "%s: res not stable!\n", __func__); + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(rk628_hdmirx_get_timings); + +u8 rk628_hdmirx_get_range(struct rk628 *rk628) +{ + u32 val; + u8 color_range; + + rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &val); + color_range = (val & RGB_COLORRANGE_MASK) >> 18; + if (color_range == 0x1) + color_range = CSC_LIMIT_RANGE; + else + color_range = CSC_FULL_RANGE; + + return color_range; +} +EXPORT_SYMBOL(rk628_hdmirx_get_range); + +void rk628_hdmirx_controller_reset(struct rk628 *rk628) +{ + mutex_lock(&rk628->rst_lock); + rk628_control_assert(rk628, RGU_HDMIRX); + rk628_control_assert(rk628, RGU_HDMIRX_PON); + udelay(10); + rk628_control_deassert(rk628, RGU_HDMIRX); + rk628_control_deassert(rk628, RGU_HDMIRX_PON); + udelay(10); + rk628_i2c_write(rk628, HDMI_RX_DMI_SW_RST, 0x000101ff); + rk628_i2c_write(rk628, HDMI_RX_DMI_DISABLE_IF, 0x00000000); + rk628_i2c_write(rk628, HDMI_RX_DMI_DISABLE_IF, 0x0000017f); + rk628_i2c_write(rk628, HDMI_RX_DMI_DISABLE_IF, 0x0001017f); + mutex_unlock(&rk628->rst_lock); +} +EXPORT_SYMBOL(rk628_hdmirx_controller_reset); + +bool rk628_hdmirx_scdc_ced_err(struct rk628 *rk628) +{ + u32 val, val1; + + if (rk628->version < RK628F_VERSION) + return false; + + rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS1, &val); + rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS2, &val1); + if (((val >> 15) & SCDC_ERRDET_MASK) < SCDC_CED_ERR_CNT && + ((val1 >> 15) & SCDC_ERRDET_MASK) < SCDC_CED_ERR_CNT && + (val1 & SCDC_ERRDET_MASK) < SCDC_CED_ERR_CNT) + return false; + + dev_info(rk628->dev, "%s: Character Error(0x%x 0x%x)!\n", __func__, val, val1); + return true; +} +EXPORT_SYMBOL(rk628_hdmirx_scdc_ced_err); diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.h b/drivers/media/i2c/rk628/rk628_hdmirx.h index cbc3736e9475..801ae63b7705 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.h +++ b/drivers/media/i2c/rk628/rk628_hdmirx.h @@ -8,6 +8,7 @@ #ifndef __RK628_HDMIRX_H #define __RK628_HDMIRX_H +#include #include "rk628.h" /* --------- EDID and HDCP KEY ------- */ @@ -32,6 +33,8 @@ #define HOT_PLUG_DETECT_MASK BIT(0) #define HOT_PLUG_DETECT(x) UPDATE(x, 0, 0) #define HDMI_RX_HDMI_TIMER_CTRL (HDMI_RX_BASE + 0x0008) +#define VIDEO_PERIOD_MASK BIT(11) +#define VIDEO_PERIOD(x) UPDATE(x, 11, 11) #define HDMI_RX_HDMI_RES_OVR (HDMI_RX_BASE + 0x0010) #define HDMI_RX_HDMI_PLL_FRQSET2 (HDMI_RX_BASE + 0x0020) #define HDMI_RX_HDMI_PCB_CTRL (HDMI_RX_BASE + 0x0038) @@ -81,6 +84,7 @@ #define DCM_COLOUR_DEPTH_SEL(x) UPDATE(x, 12, 12) #define DCM_COLOUR_DEPTH(x) UPDATE(x, 11, 8) #define DCM_GCP_ZERO_FIELDS(x) UPDATE(x, 5, 2) +#define HDMI_VM_CFG_CH0_1 (HDMI_RX_BASE + 0x00b0) #define HDMI_VM_CFG_CH2 (HDMI_RX_BASE + 0x00b4) #define HDMI_RX_HDCP_CTRL (HDMI_RX_BASE + 0x00c0) #define HDCP_ENABLE_MASK BIT(24) @@ -189,6 +193,15 @@ #define HDMI_RX_AUDPLL_GEN_N (HDMI_RX_BASE + 0x0284) #define HDMI_RX_SNPS_PHYG3_CTRL (HDMI_RX_BASE + 0x02c0) #define PORTSELECT(x) UPDATE(x, 3, 2) +#define HDMI_RX_I2CM_PHYG3_SLAVE (HDMI_RX_BASE + 0x02c4) +#define HDMI_RX_I2CM_PHYG3_ADDRESS (HDMI_RX_BASE + 0x02c8) +#define HDMI_RX_I2CM_PHYG3_DATAO (HDMI_RX_BASE + 0x02cc) +#define HDMI_RX_I2CM_PHYG3_DATAI (HDMI_RX_BASE + 0x02d0) +#define HDMI_RX_I2CM_PHYG3_OPERATION (HDMI_RX_BASE + 0x02d4) +#define HDMI_RX_I2CM_PHYG3_MODE (HDMI_RX_BASE + 0x02d8) +#define HDMI_RX_I2CM_PHYG3_SS_CNTS (HDMI_RX_BASE + 0x02e0) +#define HDMI_RX_I2CM_PHYG3_FS_HCNT (HDMI_RX_BASE + 0x02e4) + #define HDMI_RX_PDEC_CTRL (HDMI_RX_BASE + 0x0300) #define PFIFO_STORE_FILTER_EN_MASK BIT(31) #define PFIFO_STORE_FILTER_EN(x) UPDATE(x, 31, 31) @@ -234,9 +247,12 @@ #define HDMI_RX_PDEC_STS (HDMI_RX_BASE + 0x0360) #define DVI_DET BIT(28) #define HDMI_RX_PDEC_GCP_AVMUTE (HDMI_RX_BASE + 0x0380) +#define PKTDEC_GCP_CD_MASK GENMASK(7, 4) #define HDMI_RX_PDEC_AVI_PB (HDMI_RX_BASE + 0x03a4) #define VIDEO_FORMAT_MASK GENMASK(6, 5) #define VIDEO_FORMAT(x) UPDATE(x, 6, 5) +#define RGB_COLORRANGE_MASK GENMASK(19, 18) +#define RGB_COLORRANGE(x) UPDATE(x, 19, 18) #define ACT_INFO_PRESENT_MASK BIT(4) #define HDMI_RX_PDEC_ACR_CTS (HDMI_RX_BASE + 0x0390) #define HDMI_RX_PDEC_ACR_N (HDMI_RX_BASE + 0x0394) @@ -248,6 +264,7 @@ #define PVO1UNMUTE(x) UPDATE(x, 29, 29) #define PIXELMODE(x) UPDATE(x, 28, 28) #define CTRLCHECKEN(x) UPDATE(x, 8, 8) +#define SCDC_ENABLE_MASK BIT(4) #define SCDC_ENABLE(x) UPDATE(x, 4, 4) #define SCRAMBEN_SEL(x) UPDATE(x, 1, 0) #define HDMI_RX_SCDC_I2CCONFIG (HDMI_RX_BASE + 0x0804) @@ -259,16 +276,21 @@ #define MILISECTIMERLIMIT(x) UPDATE(x, 15, 0) #define HDMI_RX_HDCP22_CONTROL (HDMI_RX_BASE + 0x081c) #define HDMI_RX_SCDC_REGS0 (HDMI_RX_BASE + 0x0820) +#define SCDC_TMDSBITCLKRATIO BIT(17) #define HDMI_RX_SCDC_REGS1 (HDMI_RX_BASE + 0x0824) #define HDMI_RX_SCDC_REGS2 (HDMI_RX_BASE + 0x0828) +#define SCDC_ERRDET_MASK GENMASK(14, 0) #define HDMI_RX_SCDC_REGS3 (HDMI_RX_BASE + 0x082c) #define HDMI_RX_SCDC_WRDATA0 (HDMI_RX_BASE + 0x0860) #define MANUFACTUREROUI(x) UPDATE(x, 31, 8) #define SINKVERSION(x) UPDATE(x, 7, 0) +#define HDMI_RX_HDMI20_STATUS (HDMI_RX_BASE + 0x08e0) +#define SCRAMBDET_MASK BIT(0) #define HDMI_RX_HDMI2_IEN_CLR (HDMI_RX_BASE + 0x0f60) #define HDMI_RX_HDMI2_ISTS (HDMI_RX_BASE + 0x0f68) #define HDMI_RX_PDEC_IEN_CLR (HDMI_RX_BASE + 0x0f78) +#define AVI_CKS_CHG_ICLR BIT(24) #define ACR_N_CHG_ICLR BIT(23) #define ACR_CTS_CHG_ICLR BIT(22) #define GCP_AV_MUTE_CHG_ENCLR BIT(21) @@ -284,6 +306,7 @@ #define GCP_RCV_ENSET BIT(16) #define AMP_RCV_ENSET BIT(14) #define HDMI_RX_PDEC_ISTS (HDMI_RX_BASE + 0x0f80) +#define AVI_CKS_CHG_ISTS BIT(24) #define GCP_AV_MUTE_CHG_ISTS BIT(21) #define AIF_RCV_ISTS BIT(19) #define AVI_RCV_ISTS BIT(18) @@ -359,6 +382,43 @@ #define HDMIRX_HDCP1X_ID 13 +#define HDMIRX_GET_TIMING_CNT 20 +#define HDMIRX_MODETCLK_CNT_NUM 1000 +#define HDMIRX_MODETCLK_HZ 49500000 + +#define EDID_NUM_BLOCKS_MAX 2 +#define EDID_BLOCK_SIZE 128 + +#define RK628_CSI_LINK_FREQ_LOW 350000000 +#define RK628_CSI_LINK_FREQ_HIGH 650000000 +#define RK628_CSI_LINK_FREQ_925M 925000000 +#define RK628_CSI_PIXEL_RATE_LOW 400000000 +#define RK628_CSI_PIXEL_RATE_HIGH 600000000 +#define MIPI_DATARATE_MBPS_LOW 700 +#define MIPI_DATARATE_MBPS_HIGH 1300 + +#define POLL_INTERVAL_MS 1000 +#define RXPHY_CFG_MAX_TIMES 10 +#define CSITX_ERR_RETRY_TIMES 3 + +#define USE_4_LANES 4 +#define YUV422_8BIT 0x1e + +#define SCDC_CED_ERR_CNT 0xfff + +enum color_range { + CSC_LIMIT_RANGE, + CSC_FULL_RANGE, +}; + +enum bus_format { + BUS_FMT_RGB = 0, + BUS_FMT_YUV422 = 1, + BUS_FMT_YUV444 = 2, + BUS_FMT_YUV420 = 3, + BUS_FMT_UNKNOWN, +}; + struct hdcp_keys { u8 KSV[HDCP_KEY_KSV_SIZE]; u8 devicekey[HDCP_PRIVATE_KEY_SIZE]; @@ -392,5 +452,16 @@ bool rk628_audio_ctsnints_enabled(HAUDINFO info); void rk628_csi_isr_ctsn(HAUDINFO info, u32 pdec_ints); void rk628_csi_isr_fifoints(HAUDINFO info, u32 fifo_ints); int rk628_is_avi_ready(struct rk628 *rk628, bool avi_rcv_rdy); +void rk628_hdmirx_verisyno_phy_power_on(struct rk628 *rk628); +void rk628_hdmirx_phy_prepclk_cfg(struct rk628 *rk628); +int rk628_hdmirx_verisyno_phy_init(struct rk628 *rk628); +u8 rk628_hdmirx_get_format(struct rk628 *rk628); +void rk628_set_bg_enable(struct rk628 *rk628, bool en); +u32 rk628_hdmirx_get_tmdsclk_cnt(struct rk628 *rk628); +int rk628_hdmirx_get_timings(struct rk628 *rk628, + struct v4l2_dv_timings *timings); +u8 rk628_hdmirx_get_range(struct rk628 *rk628); +void rk628_hdmirx_controller_reset(struct rk628 *rk628); +bool rk628_hdmirx_scdc_ced_err(struct rk628 *rk628); #endif diff --git a/drivers/media/i2c/rk628/rk628_mipi_dphy.c b/drivers/media/i2c/rk628/rk628_mipi_dphy.c new file mode 100644 index 000000000000..8a9fe28458fa --- /dev/null +++ b/drivers/media/i2c/rk628/rk628_mipi_dphy.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Rockchip Electronics Co. Ltd. + * + */ + +#include +#include +#include + +#include "rk628_csi.h" +#include "rk628_dsi.h" +#include "rk628_mipi_dphy.h" + +static inline void testif_testclk_assert(struct rk628 *rk628, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON, + PHY_TESTCLK, PHY_TESTCLK); + udelay(1); +} + +static inline void testif_testclk_deassert(struct rk628 *rk628, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON, + PHY_TESTCLK, 0); + udelay(1); +} + +void rk628_testif_testclr_assert(struct rk628 *rk628, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON, + PHY_TESTCLR, PHY_TESTCLR); + udelay(1); +} +EXPORT_SYMBOL(rk628_testif_testclr_assert); + +void rk628_testif_testclr_deassert(struct rk628 *rk628, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON, + PHY_TESTCLR, 0); + udelay(1); +} +EXPORT_SYMBOL(rk628_testif_testclr_deassert); + +static inline void testif_testen_assert(struct rk628 *rk628, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON, + PHY_TESTEN, PHY_TESTEN); + udelay(1); +} + +static inline void testif_testen_deassert(struct rk628 *rk628, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON, + PHY_TESTEN, 0); + udelay(1); +} + +static inline void testif_set_data(struct rk628 *rk628, u8 data, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON, + PHY_TESTDIN_MASK, PHY_TESTDIN(data)); + udelay(1); +} + +static inline u8 testif_get_data(struct rk628 *rk628, uint8_t mipi_id) +{ + u32 data = 0; + + rk628_i2c_read(rk628, mipi_id ? GRF_DPHY1_STATUS : GRF_DPHY0_STATUS, &data); + + return data >> PHY_TESTDOUT_SHIFT; +} + +static void testif_test_code_write(struct rk628 *rk628, u8 test_code, uint8_t mipi_id) +{ + testif_testclk_assert(rk628, mipi_id); + testif_set_data(rk628, test_code, mipi_id); + testif_testen_assert(rk628, mipi_id); + testif_testclk_deassert(rk628, mipi_id); + testif_testen_deassert(rk628, mipi_id); +} + +static void testif_test_data_write(struct rk628 *rk628, u8 test_data, uint8_t mipi_id) +{ + testif_testclk_deassert(rk628, mipi_id); + testif_set_data(rk628, test_data, mipi_id); + testif_testclk_assert(rk628, mipi_id); +} + +u8 rk628_testif_write(struct rk628 *rk628, u8 test_code, u8 test_data, uint8_t mipi_id) +{ + u8 monitor_data; + + testif_test_code_write(rk628, test_code, mipi_id); + testif_test_data_write(rk628, test_data, mipi_id); + monitor_data = testif_get_data(rk628, mipi_id); + + dev_dbg(rk628->dev, "test_code=0x%02x, mipi dphy%x", test_code, mipi_id); + dev_dbg(rk628->dev, "test_data=0x%02x, mipi dphy%x", test_data, mipi_id); + dev_dbg(rk628->dev, "monitor_data=0x%02x, mipi dphy%x\n", monitor_data, mipi_id); + + return monitor_data; +} +EXPORT_SYMBOL(rk628_testif_write); + +u8 rk628_testif_read(struct rk628 *rk628, u8 test_code, uint8_t mipi_id) +{ + u8 test_data; + + testif_test_code_write(rk628, test_code, mipi_id); + test_data = testif_get_data(rk628, mipi_id); + testif_test_data_write(rk628, test_data, mipi_id); + + return test_data; +} +EXPORT_SYMBOL(rk628_testif_read); + +static inline void mipi_dphy_enablelane_assert(struct rk628 *rk628, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? CSITX1_DPHY_CTRL : CSITX_DPHY_CTRL, + CSI_DPHY_EN_MASK, CSI_DPHY_EN(rk628->dphy_lane_en)); + udelay(1); +} + +static inline void mipi_dphy_enablelane_deassert(struct rk628 *rk628, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? CSITX1_DPHY_CTRL : CSITX_DPHY_CTRL, + CSI_DPHY_EN_MASK, 0); + udelay(1); +} + +static inline void mipi_dphy_shutdownz_assert(struct rk628 *rk628) +{ + rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, CSI_PHYSHUTDOWNZ, 0); + udelay(1); +} + +static inline void mipi_dphy_shutdownz_deassert(struct rk628 *rk628) +{ + rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, CSI_PHYSHUTDOWNZ, + CSI_PHYSHUTDOWNZ); + udelay(1); +} + +static inline void mipi_dphy_rstz_assert(struct rk628 *rk628) +{ + rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, CSI_PHYRSTZ, 0); + udelay(1); +} + +static inline void mipi_dphy_rstz_deassert(struct rk628 *rk628) +{ + rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, CSI_PHYRSTZ, + CSI_PHYRSTZ); + udelay(1); +} + +void rk628_mipi_dphy_init_hsfreqrange(struct rk628 *rk628, int lane_mbps, uint8_t mipi_id) +{ + const struct { + unsigned long max_lane_mbps; + u8 hsfreqrange; + } hsfreqrange_table[] = { + { 90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01}, + { 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12}, + { 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23}, + { 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15}, + { 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07}, + { 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09}, + { 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a}, + {1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b}, + {1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c}, + {1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c} + }; + u8 hsfreqrange; + unsigned int index; + + for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++) + if (lane_mbps <= hsfreqrange_table[index].max_lane_mbps) + break; + + if (index == ARRAY_SIZE(hsfreqrange_table)) + --index; + + hsfreqrange = hsfreqrange_table[index].hsfreqrange; + rk628_testif_write(rk628, 0x44, HSFREQRANGE(hsfreqrange), mipi_id); +} +EXPORT_SYMBOL(rk628_mipi_dphy_init_hsfreqrange); + +void rk628_mipi_dphy_init_hsmanual(struct rk628 *rk628, bool manual, uint8_t mipi_id) +{ + dev_info(rk628->dev, + "mipi dphy%d hs config, manual: %s\n", mipi_id, manual ? "true" : "false"); + //config mipi timing when mipi freq is 1250Mbps + rk628_testif_write(rk628, 0x71, + manual ? (HSTX(rk628->mipi_timing[mipi_id].data_prepare) | BIT(7)) : 0, mipi_id); + usleep_range(1500, 2000); + rk628_testif_write(rk628, 0x72, + manual ? (HSZERO(rk628->mipi_timing[mipi_id].data_zero) | BIT(6)) : 0, mipi_id); + usleep_range(1500, 2000); + rk628_testif_write(rk628, 0x73, + manual ? (HSTX(rk628->mipi_timing[mipi_id].data_trail) | BIT(7)) : 0, mipi_id); + usleep_range(1500, 2000); + rk628_testif_write(rk628, 0x61, + manual ? (HSTX(rk628->mipi_timing[mipi_id].clk_prepare) | BIT(7)) : 0, mipi_id); + usleep_range(1500, 2000); + rk628_testif_write(rk628, 0x62, + manual ? (HSZERO(rk628->mipi_timing[mipi_id].clk_zero) | BIT(6)) : 0, mipi_id); + usleep_range(1500, 2000); + rk628_testif_write(rk628, 0x63, + manual ? (HSTX(rk628->mipi_timing[mipi_id].clk_trail) | BIT(7)) : 0, mipi_id); + usleep_range(1500, 2000); + rk628_testif_write(rk628, 0x65, + manual ? (HSPOST(rk628->mipi_timing[mipi_id].clk_post) | BIT(5)) : 0, mipi_id); +} +EXPORT_SYMBOL(rk628_mipi_dphy_init_hsmanual); + +int rk628_mipi_dphy_reset(struct rk628 *rk628) +{ + u32 val, mask; + int ret; + + rk628_i2c_write(rk628, CSITX_SYS_CTRL0_IMD, 0x1); + if (rk628->version >= RK628F_VERSION) + rk628_i2c_write(rk628, CSITX1_SYS_CTRL0_IMD, 0x1); + mipi_dphy_enablelane_deassert(rk628, 0); + if (rk628->version >= RK628F_VERSION) + mipi_dphy_enablelane_deassert(rk628, 1); + mipi_dphy_shutdownz_assert(rk628); + mipi_dphy_rstz_assert(rk628); + rk628_testif_testclr_assert(rk628, 0); + if (rk628->version >= RK628F_VERSION) + rk628_testif_testclr_assert(rk628, 1); + + /* Set all REQUEST inputs to zero */ + rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, + FORCETXSTOPMODE_MASK | FORCERXMODE_MASK, + FORCETXSTOPMODE(0) | FORCERXMODE(0)); + if (rk628->version >= RK628F_VERSION) + rk628_i2c_update_bits(rk628, GRF_MIPI_TX1_CON, + FORCETXSTOPMODE_MASK | FORCERXMODE_MASK, + FORCETXSTOPMODE(0) | FORCERXMODE(0)); + udelay(1); + rk628_testif_testclr_deassert(rk628, 0); + if (rk628->version >= RK628F_VERSION) + rk628_testif_testclr_deassert(rk628, 1); + mipi_dphy_enablelane_assert(rk628, 0); + if (rk628->version >= RK628F_VERSION) + mipi_dphy_enablelane_assert(rk628, 1); + mipi_dphy_shutdownz_deassert(rk628); + mipi_dphy_rstz_deassert(rk628); + rk628_i2c_write(rk628, CSITX_SYS_CTRL0_IMD, 0x0); + if (rk628->version >= RK628F_VERSION) + rk628_i2c_write(rk628, CSITX1_SYS_CTRL0_IMD, 0x0); + usleep_range(10000, 11000); + + mask = STOPSTATE_CLK | STOPSTATE_LANE0; + + ret = regmap_read_poll_timeout(rk628->regmap[RK628_DEV_CSI], + CSITX_CSITX_STATUS1, + val, (val & mask) == mask, + 0, 1000); + if (ret < 0) + dev_err(rk628->dev, "csi0 lane module is not in stop state, val: 0x%x\n", val); + + if (rk628->version >= RK628F_VERSION) { + ret = regmap_read_poll_timeout(rk628->regmap[RK628_DEV_CSI1], + CSITX1_CSITX_STATUS1, + val, (val & mask) == mask, + 0, 1000); + if (ret < 0) + dev_err(rk628->dev, + "csi1 lane module is not in stop state, val: 0x%x\n", val); + } + + return 0; +} +EXPORT_SYMBOL(rk628_mipi_dphy_reset); diff --git a/drivers/media/i2c/rk628/rk628_mipi_dphy.h b/drivers/media/i2c/rk628/rk628_mipi_dphy.h index 31331f41c773..531dfe440415 100644 --- a/drivers/media/i2c/rk628/rk628_mipi_dphy.h +++ b/drivers/media/i2c/rk628/rk628_mipi_dphy.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2020 Rockchip Electronics Co. Ltd. + * Copyright (c) 2023 Rockchip Electronics Co. Ltd. * * Author: Shunqing Chen */ @@ -8,12 +8,6 @@ #ifndef _RK628_MIPI_DPHY_H #define _RK628_MIPI_DPHY_H -#include -#include -#include - -#include "rk628_csi.h" -#include "rk628_dsi.h" #include "rk628.h" /* Test Code: 0x44 (HS RX Control of Lane 0) */ @@ -21,239 +15,14 @@ #define HSTX(x) UPDATE(x, 6, 0) #define HSZERO(x) UPDATE(x, 5, 0) #define HSPOST(x) UPDATE(x, 4, 0) +#define HSEXIT(x) UPDATE(x, 4, 0) -static inline void testif_testclk_assert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, - PHY_TESTCLK, PHY_TESTCLK); - udelay(1); -} - -static inline void testif_testclk_deassert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, - PHY_TESTCLK, 0); - udelay(1); -} - -static inline void testif_testclr_assert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, - PHY_TESTCLR, PHY_TESTCLR); - udelay(1); -} - -static inline void testif_testclr_deassert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, - PHY_TESTCLR, 0); - udelay(1); -} - -static inline void testif_testen_assert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, - PHY_TESTEN, PHY_TESTEN); - udelay(1); -} - -static inline void testif_testen_deassert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, - PHY_TESTEN, 0); - udelay(1); -} - -static inline void testif_set_data(struct rk628 *rk628, u8 data) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, - PHY_TESTDIN_MASK, PHY_TESTDIN(data)); - udelay(1); -} - -static inline u8 testif_get_data(struct rk628 *rk628) -{ - u32 data = 0; - - rk628_i2c_read(rk628, GRF_DPHY0_STATUS, &data); - - return data >> PHY_TESTDOUT_SHIFT; -} - -static void testif_test_code_write(struct rk628 *rk628, u8 test_code) -{ - testif_testclk_assert(rk628); - testif_set_data(rk628, test_code); - testif_testen_assert(rk628); - testif_testclk_deassert(rk628); - testif_testen_deassert(rk628); -} - -static void testif_test_data_write(struct rk628 *rk628, u8 test_data) -{ - testif_testclk_deassert(rk628); - testif_set_data(rk628, test_data); - testif_testclk_assert(rk628); -} - -static u8 testif_write(struct rk628 *rk628, u8 test_code, u8 test_data) -{ - u8 monitor_data; - - testif_test_code_write(rk628, test_code); - testif_test_data_write(rk628, test_data); - monitor_data = testif_get_data(rk628); - - dev_dbg(rk628->dev, "test_code=0x%02x, ", test_code); - dev_dbg(rk628->dev, "test_data=0x%02x, ", test_data); - dev_dbg(rk628->dev, "monitor_data=0x%02x\n", monitor_data); - - return monitor_data; -} - -static inline u8 testif_read(struct rk628 *rk628, u8 test_code) -{ - u8 test_data; - - testif_test_code_write(rk628, test_code); - test_data = testif_get_data(rk628); - testif_test_data_write(rk628, test_data); - - return test_data; -} - -static inline void mipi_dphy_enableclk_assert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, CSITX_DPHY_CTRL, DPHY_ENABLECLK, - DPHY_ENABLECLK); - udelay(1); -} - -static inline void mipi_dphy_enableclk_deassert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, CSITX_DPHY_CTRL, DPHY_ENABLECLK, 0); - udelay(1); -} - -static inline void mipi_dphy_shutdownz_assert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, CSI_PHYSHUTDOWNZ, 0); - udelay(1); -} - -static inline void mipi_dphy_shutdownz_deassert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, CSI_PHYSHUTDOWNZ, - CSI_PHYSHUTDOWNZ); - udelay(1); -} - -static inline void mipi_dphy_rstz_assert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, CSI_PHYRSTZ, 0); - udelay(1); -} - -static inline void mipi_dphy_rstz_deassert(struct rk628 *rk628) -{ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, CSI_PHYRSTZ, - CSI_PHYRSTZ); - udelay(1); -} - -static inline void mipi_dphy_init_hsfreqrange(struct rk628 *rk628, int lane_mbps) -{ - const struct { - unsigned long max_lane_mbps; - u8 hsfreqrange; - } hsfreqrange_table[] = { - { 90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01}, - { 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12}, - { 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23}, - { 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15}, - { 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07}, - { 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09}, - { 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a}, - {1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b}, - {1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c}, - {1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c} - }; - u8 hsfreqrange; - unsigned int index; - - for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++) - if (lane_mbps <= hsfreqrange_table[index].max_lane_mbps) - break; - - if (index == ARRAY_SIZE(hsfreqrange_table)) - --index; - - hsfreqrange = hsfreqrange_table[index].hsfreqrange; - testif_write(rk628, 0x44, HSFREQRANGE(hsfreqrange)); -} - -static void __maybe_unused mipi_dphy_init_hsmanual(struct rk628 *rk628, bool manual) -{ - if (manual) { - //config mipi timing when mipi freq is 1250Mbps - testif_write(rk628, 0x71, HSTX(0x4a) | BIT(7)); - usleep_range(1500, 2000); - testif_write(rk628, 0x72, HSZERO(0xf) | BIT(6)); - usleep_range(1500, 2000); - testif_write(rk628, 0x73, HSTX(0x5d) | BIT(7)); - usleep_range(1500, 2000); - testif_write(rk628, 0x61, HSTX(0x3a) | BIT(7)); - usleep_range(1500, 2000); - testif_write(rk628, 0x62, HSZERO(0x3a) | BIT(6)); - usleep_range(1500, 2000); - testif_write(rk628, 0x63, HSTX(0x5a) | BIT(7)); - usleep_range(1500, 2000); - testif_write(rk628, 0x65, HSPOST(0x1f) | BIT(5)); - } else { - testif_write(rk628, 0x71, 0); - usleep_range(1500, 2000); - testif_write(rk628, 0x72, 0); - usleep_range(1500, 2000); - testif_write(rk628, 0x73, 0); - usleep_range(1500, 2000); - testif_write(rk628, 0x61, 0); - usleep_range(1500, 2000); - testif_write(rk628, 0x62, 0); - usleep_range(1500, 2000); - testif_write(rk628, 0x63, 0); - usleep_range(1500, 2000); - testif_write(rk628, 0x65, 0); - } -} - -static inline int mipi_dphy_reset(struct rk628 *rk628) -{ - u32 val, mask; - - mipi_dphy_enableclk_deassert(rk628); - mipi_dphy_shutdownz_assert(rk628); - mipi_dphy_rstz_assert(rk628); - testif_testclr_assert(rk628); - - /* Set all REQUEST inputs to zero */ - rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, - FORCETXSTOPMODE_MASK | FORCERXMODE_MASK, - FORCETXSTOPMODE(0) | FORCERXMODE(0)); - udelay(1); - testif_testclr_deassert(rk628); - mipi_dphy_enableclk_assert(rk628); - mipi_dphy_shutdownz_deassert(rk628); - mipi_dphy_rstz_deassert(rk628); - usleep_range(1500, 2000); - - mask = STOPSTATE_CLK | STOPSTATE_LANE0; - rk628_i2c_read(rk628, CSITX_CSITX_STATUS1, &val); - if ((val & mask) != mask) { - dev_err(rk628->dev, "lane module is not in stop state\n"); - return -1; - } - - return 0; -} +void rk628_testif_testclr_deassert(struct rk628 *rk628, uint8_t mipi_id); +void rk628_testif_testclr_assert(struct rk628 *rk628, uint8_t mipi_id); +u8 rk628_testif_write(struct rk628 *rk628, u8 test_code, u8 test_data, uint8_t mipi_id); +u8 rk628_testif_read(struct rk628 *rk628, u8 test_code, uint8_t mipi_id); +void rk628_mipi_dphy_init_hsfreqrange(struct rk628 *rk628, int lane_mbps, uint8_t mipi_id); +void rk628_mipi_dphy_init_hsmanual(struct rk628 *rk628, bool manual, uint8_t mipi_id); +int rk628_mipi_dphy_reset(struct rk628 *rk628); #endif diff --git a/drivers/media/i2c/rk628/rk628_post_process.c b/drivers/media/i2c/rk628/rk628_post_process.c new file mode 100644 index 000000000000..6126bfbf13d6 --- /dev/null +++ b/drivers/media/i2c/rk628/rk628_post_process.c @@ -0,0 +1,1356 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Rockchip Electronics Co. Ltd. + * + */ +#include "rk628.h" +#include "rk628_cru.h" +#include "rk628_hdmirx.h" +#include "rk628_post_process.h" +#include + +#define PQ_CSC_HUE_TABLE_NUM 256 +#define PQ_CSC_MODE_COEF_COMMENT_LEN 32 +#define PQ_CSC_SIMPLE_MAT_PARAM_FIX_BIT_WIDTH 10 +#define PQ_CSC_SIMPLE_MAT_PARAM_FIX_NUM (1 << PQ_CSC_SIMPLE_MAT_PARAM_FIX_BIT_WIDTH) + +#define PQ_CALC_ENHANCE_BIT 6 +/* csc convert coef fixed-point num bit width */ +#define PQ_CSC_PARAM_FIX_BIT_WIDTH 10 +/* csc convert coef half fixed-point num bit width */ +#define PQ_CSC_PARAM_HALF_FIX_BIT_WIDTH (PQ_CSC_PARAM_FIX_BIT_WIDTH - 1) +/* csc convert coef fixed-point num */ +#define PQ_CSC_PARAM_FIX_NUM (1 << PQ_CSC_PARAM_FIX_BIT_WIDTH) +#define PQ_CSC_PARAM_HALF_FIX_NUM (1 << PQ_CSC_PARAM_HALF_FIX_BIT_WIDTH) +/* csc input param bit width */ +#define PQ_CSC_IN_PARAM_NORM_BIT_WIDTH 9 +/* csc input param normalization coef */ +#define PQ_CSC_IN_PARAM_NORM_COEF (1 << PQ_CSC_IN_PARAM_NORM_BIT_WIDTH) + +/* csc hue table range [0,255] */ +#define PQ_CSC_HUE_TABLE_DIV_COEF 2 +/* csc brightness offset */ +#define PQ_CSC_BRIGHTNESS_OFFSET 256 + +/* dc coef base bit width */ +#define PQ_CSC_DC_COEF_BASE_BIT_WIDTH 10 +/* input dc coef offset for 10bit data */ +#define PQ_CSC_DC_IN_OFFSET 64 +/* input and output dc coef offset for 10bit data u,v */ +#define PQ_CSC_DC_IN_OUT_DEFAULT 512 +/* r,g,b color temp div coef, range [-128,128] for 10bit data */ +#define PQ_CSC_TEMP_OFFSET_DIV_COEF 2 + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define CLIP(x, min_v, max_v) MIN(MAX(x, min_v), max_v) + +#define V4L2_COLORSPACE_BT709F 0xfe +#define V4L2_COLORSPACE_BT2020F 0xff + +enum vop_csc_bit_depth { + CSC_10BIT_DEPTH, + CSC_13BIT_DEPTH, +}; + +enum rk_pq_csc_mode { + RK_PQ_CSC_YUV2RGB_601 = 0, /* YCbCr_601 LIMIT-> RGB FULL */ + RK_PQ_CSC_YUV2RGB_709, /* YCbCr_709 LIMIT-> RGB FULL */ + RK_PQ_CSC_RGB2YUV_601, /* RGB FULL->YCbCr_601 LIMIT */ + RK_PQ_CSC_RGB2YUV_709, /* RGB FULL->YCbCr_709 LIMIT */ + RK_PQ_CSC_YUV2YUV_709_601, /* YCbCr_709 LIMIT->YCbCr_601 LIMIT */ + RK_PQ_CSC_YUV2YUV_601_709, /* YCbCr_601 LIMIT->YCbCr_709 LIMIT */ + RK_PQ_CSC_YUV2YUV, /* YCbCr LIMIT->YCbCr LIMIT */ + RK_PQ_CSC_YUV2RGB_601_FULL, /* YCbCr_601 FULL-> RGB FULL */ + RK_PQ_CSC_YUV2RGB_709_FULL, /* YCbCr_709 FULL-> RGB FULL */ + RK_PQ_CSC_RGB2YUV_601_FULL, /* RGB FULL->YCbCr_601 FULL */ + RK_PQ_CSC_RGB2YUV_709_FULL, /* RGB FULL->YCbCr_709 FULL */ + RK_PQ_CSC_YUV2YUV_709_601_FULL, /* YCbCr_709 FULL->YCbCr_601 FULL */ + RK_PQ_CSC_YUV2YUV_601_709_FULL, /* YCbCr_601 FULL->YCbCr_709 FULL */ + RK_PQ_CSC_YUV2YUV_FULL, /* YCbCr FULL->YCbCr FULL */ + RK_PQ_CSC_YUV2YUV_LIMIT2FULL, /* YCbCr LIMIT->YCbCr FULL */ + RK_PQ_CSC_YUV2YUV_601_709_LIMIT2FULL, /* YCbCr 601 LIMIT->YCbCr 709 FULL */ + RK_PQ_CSC_YUV2YUV_709_601_LIMIT2FULL, /* YCbCr 709 LIMIT->YCbCr 601 FULL */ + RK_PQ_CSC_YUV2YUV_FULL2LIMIT, /* YCbCr FULL->YCbCr LIMIT */ + RK_PQ_CSC_YUV2YUV_601_709_FULL2LIMIT, /* YCbCr 601 FULL->YCbCr 709 LIMIT */ + RK_PQ_CSC_YUV2YUV_709_601_FULL2LIMIT, /* YCbCr 709 FULL->YCbCr 601 LIMIT */ + RK_PQ_CSC_YUV2RGBL_601, /* YCbCr_601 LIMIT-> RGB LIMIT */ + RK_PQ_CSC_YUV2RGBL_709, /* YCbCr_709 LIMIT-> RGB LIMIT */ + RK_PQ_CSC_RGBL2YUV_601, /* RGB LIMIT->YCbCr_601 LIMIT */ + RK_PQ_CSC_RGBL2YUV_709, /* RGB LIMIT->YCbCr_709 LIMIT */ + RK_PQ_CSC_YUV2RGBL_601_FULL, /* YCbCr_601 FULL-> RGB LIMIT */ + RK_PQ_CSC_YUV2RGBL_709_FULL, /* YCbCr_709 FULL-> RGB LIMIT */ + RK_PQ_CSC_RGBL2YUV_601_FULL, /* RGB LIMIT->YCbCr_601 FULL */ + RK_PQ_CSC_RGBL2YUV_709_FULL, /* RGB LIMIT->YCbCr_709 FULL */ + RK_PQ_CSC_RGB2RGBL, /* RGB FULL->RGB LIMIT */ + RK_PQ_CSC_RGBL2RGB, /* RGB LIMIT->RGB FULL */ + RK_PQ_CSC_RGBL2RGBL, /* RGB LIMIT->RGB LIMIT */ + RK_PQ_CSC_RGB2RGB, /* RGB FULL->RGB FULL */ + RK_PQ_CSC_YUV2RGB_2020, /* YUV 2020 FULL->RGB 2020 FULL */ + RK_PQ_CSC_RGB2YUV2020_LIMIT2FULL, /* BT2020RGBLIMIT -> BT2020YUVFULL */ + RK_PQ_CSC_RGB2YUV2020_LIMIT, /* BT2020RGBLIMIT -> BT2020YUVLIMIT */ + RK_PQ_CSC_RGB2YUV2020_FULL2LIMIT, /* BT2020RGBFULL -> BT2020YUVLIMIT */ + RK_PQ_CSC_RGB2YUV2020_FULL, /* BT2020RGBFULL -> BT2020YUVFULL */ +}; + +enum color_space_type { + OPTM_CS_E_UNKNOWN = 0, + OPTM_CS_E_ITU_R_BT_709 = 1, + OPTM_CS_E_FCC = 4, + OPTM_CS_E_ITU_R_BT_470_2_BG = 5, + OPTM_CS_E_SMPTE_170_M = 6, + OPTM_CS_E_SMPTE_240_M = 7, + OPTM_CS_E_XV_YCC_709 = OPTM_CS_E_ITU_R_BT_709, + OPTM_CS_E_XV_YCC_601 = 8, + OPTM_CS_E_RGB = 9, + OPTM_CS_E_XV_YCC_2020 = 10, + OPTM_CS_E_RGB_2020 = 11, +}; + +struct rk_pq_csc_coef { + s32 csc_coef00; + s32 csc_coef01; + s32 csc_coef02; + s32 csc_coef10; + s32 csc_coef11; + s32 csc_coef12; + s32 csc_coef20; + s32 csc_coef21; + s32 csc_coef22; +}; + +struct rk_pq_csc_ventor { + s32 csc_offset0; + s32 csc_offset1; + s32 csc_offset2; +}; + +struct rk_pq_csc_dc_coef { + s32 csc_in_dc0; + s32 csc_in_dc1; + s32 csc_in_dc2; + s32 csc_out_dc0; + s32 csc_out_dc1; + s32 csc_out_dc2; +}; + +/* color space param */ +struct rk_csc_colorspace_info { + enum color_space_type input_color_space; + enum color_space_type output_color_space; + bool in_full_range; + bool out_full_range; +}; + +struct rk_csc_mode_coef { + enum rk_pq_csc_mode csc_mode; + char c_csc_comment[PQ_CSC_MODE_COEF_COMMENT_LEN]; + const struct rk_pq_csc_coef *pst_csc_coef; + const struct rk_pq_csc_dc_coef *pst_csc_dc_coef; + struct rk_csc_colorspace_info st_csc_color_info; +}; + +/* + *CSC matrix + */ +/* xv_ycc BT.601 limit(i.e. SD) -> RGB full */ +static const struct rk_pq_csc_coef rk_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_full = { + 1196, 0, 1639, + 1196, -402, -835, + 1196, 2072, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_full = { + -64, -512, -512, + 0, 0, 0 +}; + +/* BT.709 limit(i.e. HD) -> RGB full */ +static const struct rk_pq_csc_coef rk_csc_table_hdy_cb_cr_limit_to_rgb_full = { + 1196, 0, 1841, + 1196, -219, -547, + 1196, 2169, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_limit_to_rgb_full = { + -64, -512, -512, + 0, 0, 0 +}; + +/* RGB full-> YUV601 (i.e. SD) limit */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_to_xv_yccsdy_cb_cr = { + 262, 515, 100, + -151, -297, 448, + 448, -376, -73 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_to_xv_yccsdy_cb_cr = { + 0, 0, 0, + 64, 512, 512 +}; + +/* RGB full-> YUV709 (i.e. SD) limit */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_to_hdy_cb_cr = { + 186, 627, 63, + -103, -346, 448, + 448, -407, -41 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_to_hdy_cb_cr = { + 0, 0, 0, + 64, 512, 512 +}; + +/* BT.709 (i.e. HD) -> to xv_ycc BT.601 (i.e. SD) */ +static const struct rk_pq_csc_coef rk_csc_table_hdy_cb_cr_to_xv_yccsdy_cb_cr = { + 1024, 104, 201, + 0, 1014, -113, + 0, -74, 1007 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_to_xv_yccsdy_cb_cr = { + -64, -512, -512, + 64, 512, 512 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full = { + 0, -512, -512, + 0, 512, 512 +}; + +/* xv_ycc BT.601 (i.e. SD) -> to BT.709 (i.e. HD) */ +static const struct rk_pq_csc_coef rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr = { + 1024, -121, -218, + 0, 1043, 117, + 0, 77, 1050 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr = { + -64, -512, -512, + 64, 512, 512 +}; + +/* xv_ycc BT.601 full(i.e. SD) -> RGB full */ +static const struct rk_pq_csc_coef rk_csc_table_xv_yccsdy_cb_cr_to_rgb_full = { + 1024, 0, 1436, + 1024, -352, -731, + 1024, 1815, 0 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_xv_yccsdy_cb_cr_to_rgb_full = { + 0, -512, -512, + 0, 0, 0 +}; + +/* BT.709 full(i.e. HD) -> RGB full */ +static const struct rk_pq_csc_coef rk_csc_table_hdy_cb_cr_to_rgb_full = { + 1024, 0, 1613, + 1024, -192, -479, + 1024, 1900, 0 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_to_rgb_full = { + 0, -512, -512, + 0, 0, 0 +}; + +/* RGB full-> YUV601 full(i.e. SD) */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_to_xv_yccsdy_cb_cr_full = { + 306, 601, 117, + -173, -339, 512, + 512, -429, -83 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_to_xv_yccsdy_cb_cr_full = { + 0, 0, 0, + 0, 512, 512 +}; + +/* RGB full-> YUV709 full (i.e. SD) */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_to_hdy_cb_cr_full = { + 218, 732, 74, + -117, -395, 512, + 512, -465, -47 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_to_hdy_cb_cr_full = { + 0, 0, 0, + 0, 512, 512 +}; + +/* limit -> full */ +static const struct rk_pq_csc_coef rk_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full = { + 1196, 0, 0, + 0, 1169, 0, + 0, 0, 1169 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full = { + -64, -512, -512, + 0, 512, 512 +}; + +/* 601 limit -> 709 full */ +static const struct rk_pq_csc_coef rk_csc_table_identity_601_limit_to_709_full = { + 1196, -138, -249, + 0, 1191, 134, + 0, 88, 1199 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_601_limit_to_709_full = { + -64, -512, -512, + 0, 512, 512 +}; + +/* 709 limit -> 601 full */ +static const struct rk_pq_csc_coef rk_csc_table_identity_709_limit_to_601_full = { + 1196, 119, 229, + 0, 1157, -129, + 0, -85, 1150 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_709_limit_to_601_full = { + -64, -512, -512, + 0, 512, 512 +}; + +/* full -> limit */ +static const struct rk_pq_csc_coef rk_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit = { + 877, 0, 0, + 0, 897, 0, + 0, 0, 897 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit = { + 0, -512, -512, + 64, 512, 512 +}; + +/* 601 full -> 709 limit */ +static const struct rk_pq_csc_coef rk_csc_table_identity_y_cb_cr_601_full_to_y_cb_cr_709_limit = { + 877, -106, -191, + 0, 914, 103, + 0, 67, 920 +}; +static const struct rk_pq_csc_dc_coef +rk_dc_csc_table_identity_y_cb_cr_601_full_to_y_cb_cr_709_limit = { + 0, -512, -512, + 64, 512, 512 +}; + +/* 709 full -> 601 limit */ +static const struct rk_pq_csc_coef rk_csc_table_identity_y_cb_cr_709_full_to_y_cb_cr_601_limit = { + 877, 91, 176, + 0, 888, -99, + 0, -65, 882 +}; +static const struct rk_pq_csc_dc_coef +rk_dc_csc_table_identity_y_cb_cr_709_full_to_y_cb_cr_601_limit = { + 0, -512, -512, + 64, 512, 512 +}; + +/* xv_ycc BT.601 limit(i.e. SD) -> RGB limit */ +static const struct rk_pq_csc_coef rk_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_limit = { + 1024, 0, 1404, + 1024, -344, -715, + 1024, 1774, 0 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_limit = { + -64, -512, -512, + 64, 64, 64 +}; + +/* BT.709 limit(i.e. HD) -> RGB limit */ +static const struct rk_pq_csc_coef rk_csc_table_hdy_cb_cr_limit_to_rgb_limit = { + 1024, 0, 1577, + 1024, -188, -469, + 1024, 1858, 0 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_limit_to_rgb_limit = { + -64, -512, -512, + 64, 64, 64 +}; + +/* RGB limit-> YUV601 (i.e. SD) limit */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_limit_to_xv_yccsdy_cb_cr = { + 306, 601, 117, + -177, -347, 524, + 524, -439, -85 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_limit_to_xv_yccsdy_cb_cr = { + -64, -64, -64, + 64, 512, 512 +}; + +/* RGB limit -> YUV709 (i.e. SD) limit */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_limit_to_hdy_cb_cr = { + 218, 732, 74, + -120, -404, 524, + 524, -476, -48 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_limit_to_hdy_cb_cr = { + -64, -64, -64, + 64, 512, 512 +}; + +/* xv_ycc BT.601 full(i.e. SD) -> RGB limit */ +static const struct rk_pq_csc_coef rk_csc_table_xv_yccsdy_cb_cr_to_rgb_limit = { + 877, 0, 1229, + 877, -302, -626, + 877, 1554, 0 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_xv_yccsdy_cb_cr_to_rgb_limit = { + 0, -512, -512, + 64, 64, 64 +}; + +/* BT.709 full(i.e. HD) -> RGB limit */ +static const struct rk_pq_csc_coef rk_csc_table_hdy_cb_cr_to_rgb_limit = { + 877, 0, 1381, + 877, -164, -410, + 877, 1627, 0 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_to_rgb_limit = { + 0, -512, -512, + 64, 64, 64 +}; + +/* RGB limit-> YUV601 full(i.e. SD) */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_limit_to_xv_yccsdy_cb_cr_full = { + 358, 702, 136, + -202, -396, 598, + 598, -501, -97 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_limit_to_xv_yccsdy_cb_cr_full = { + -64, -64, -64, + 0, 512, 512 +}; + +/* RGB limit-> YUV709 full (i.e. SD) */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_limit_to_hdy_cb_cr_full = { + 254, 855, 86, + -137, -461, 598, + 598, -543, -55 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_limit_to_hdy_cb_cr_full = { + -64, -64, -64, + 0, 512, 512 +}; + +/* RGB full -> RGB limit */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_to_rgb_limit = { + 877, 0, 0, + 0, 877, 0, + 0, 0, 877 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_to_rgb_limit = { + 0, 0, 0, + 64, 64, 64 +}; + +/* RGB limit -> RGB full */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_limit_to_rgb = { + 1196, 0, 0, + 0, 1196, 0, + 0, 0, 1196 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_limit_to_rgb = { + -64, -64, -64, + 0, 0, 0 +}; + +/* RGB limit/full -> RGB limit/full */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_to_rgb = { + 1024, 0, 0, + 0, 1024, 0, + 0, 0, 1024 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_to_rgb1 = { + -64, -64, -64, + 64, 64, 64 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_to_rgb2 = { + 0, 0, 0, + 0, 0, 0 +}; + +static const struct rk_pq_csc_coef rk_csc_table_identity_yuv_to_rgb_2020 = { + 1024, 0, 1510, + 1024, -169, -585, + 1024, 1927, 0 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_yuv_to_rgb_2020 = { + 0, -512, -512, + 0, 0, 0 +}; + +/* 2020 RGB LIMIT ->YUV LIMIT */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_limit_to_yuv_limit_2020 = { + 269, 694, 61, + -146, -377, 524, + 524, -482, -42 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_limit_to_yuv_limit_2020 = { + -64, -64, -64, + 64, 512, 512 +}; + +/* 2020 RGB LIMIT ->YUV FULL */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_limit_to_yuv_full_2020 = { + 314, 811, 71, + -167, -431, 598, + 598, -550, -48 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_limit_to_yuv_full_2020 = { + -64, -64, -64, + 0, 512, 512 +}; + +/* 2020 RGB FULL ->YUV LIMIT */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_full_to_yuv_limit_2020 = { + 230, 595, 52, + -125, -323, 448, + 448, -412, -36 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_full_to_yuv_limit_2020 = { + 0, 0, 0, + 64, 512, 512 +}; + +/* 2020 RGB FULL ->YUV FULL */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_full_to_yuv_full_2020 = { + 269, 694, 61, + -143, -369, 512, + 512, -471, -41 +}; +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_full_to_yuv_full_2020 = { + 0, 0, 0, + 0, 512, 512 +}; + +/* identity matrix */ +static const struct rk_pq_csc_coef rk_csc_table_identity_y_cb_cr_to_y_cb_cr = { + 1024, 0, 0, + 0, 1024, 0, + 0, 0, 1024 +}; + +/* + *CSC Param Struct + */ +static const struct rk_csc_mode_coef g_mode_csc_coef[] = { + { + RK_PQ_CSC_YUV2RGB_601, "YUV601 L->RGB F", + &rk_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_full, + &rk_dc_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_RGB, false, true + } + }, + { + RK_PQ_CSC_YUV2RGB_709, "YUV709 L->RGB F", + &rk_csc_table_hdy_cb_cr_limit_to_rgb_full, + &rk_dc_csc_table_hdy_cb_cr_limit_to_rgb_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_RGB, false, true + } + }, + { + RK_PQ_CSC_RGB2YUV_601, "RGB F->YUV601 L", + &rk_csc_table_rgb_to_xv_yccsdy_cb_cr, + &rk_dc_csc_table_rgb_to_xv_yccsdy_cb_cr, + { + OPTM_CS_E_RGB, OPTM_CS_E_XV_YCC_601, true, false + } + }, + { + RK_PQ_CSC_RGB2YUV_709, "RGB F->YUV709 L", + &rk_csc_table_rgb_to_hdy_cb_cr, + &rk_dc_csc_table_rgb_to_hdy_cb_cr, + { + OPTM_CS_E_RGB, OPTM_CS_E_ITU_R_BT_709, true, false + } + }, + { + RK_PQ_CSC_YUV2YUV_709_601, "YUV709 L->YUV601 L", + &rk_csc_table_hdy_cb_cr_to_xv_yccsdy_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_to_xv_yccsdy_cb_cr, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_XV_YCC_601, false, false + } + }, + { + RK_PQ_CSC_YUV2YUV_601_709, "YUV601 L->YUV709 L", + &rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_ITU_R_BT_709, false, false + } + }, + { + RK_PQ_CSC_YUV2YUV, "YUV L->YUV L", + &rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_ITU_R_BT_709, false, false + } + }, + { + RK_PQ_CSC_YUV2RGB_601_FULL, "YUV601 F->RGB F", + &rk_csc_table_xv_yccsdy_cb_cr_to_rgb_full, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_rgb_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_RGB, true, true + } + }, + { + RK_PQ_CSC_YUV2RGB_709_FULL, "YUV709 F->RGB F", + &rk_csc_table_hdy_cb_cr_to_rgb_full, + &rk_dc_csc_table_hdy_cb_cr_to_rgb_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_RGB, true, true + } + }, + { + RK_PQ_CSC_RGB2YUV_601_FULL, "RGB F->YUV601 F", + &rk_csc_table_rgb_to_xv_yccsdy_cb_cr_full, + &rk_dc_csc_table_rgb_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_RGB, OPTM_CS_E_XV_YCC_601, true, true + } + }, + { + RK_PQ_CSC_RGB2YUV_709_FULL, "RGB F->YUV709 F", + &rk_csc_table_rgb_to_hdy_cb_cr_full, + &rk_dc_csc_table_rgb_to_hdy_cb_cr_full, + { + OPTM_CS_E_RGB, OPTM_CS_E_ITU_R_BT_709, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_709_601_FULL, "YUV709 F->YUV601 F", + &rk_csc_table_hdy_cb_cr_to_xv_yccsdy_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_XV_YCC_601, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_601_709_FULL, "YUV601 F->YUV709 F", + &rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_ITU_R_BT_709, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL, "YUV F->YUV F", + &rk_csc_table_identity_y_cb_cr_to_y_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_ITU_R_BT_709, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_LIMIT2FULL, "YUV L->YUV F", + &rk_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + &rk_dc_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_ITU_R_BT_709, false, true + } + }, + { + RK_PQ_CSC_YUV2YUV_601_709_LIMIT2FULL, "YUV601 L->YUV709 F", + &rk_csc_table_identity_601_limit_to_709_full, + &rk_dc_csc_table_identity_601_limit_to_709_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_ITU_R_BT_709, false, true + } + }, + { + RK_PQ_CSC_YUV2YUV_709_601_LIMIT2FULL, "YUV709 L->YUV601 F", + &rk_csc_table_identity_709_limit_to_601_full, + &rk_dc_csc_table_identity_709_limit_to_601_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_XV_YCC_601, false, true + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL2LIMIT, "YUV F->YUV L", + &rk_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + &rk_dc_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_ITU_R_BT_709, true, false + } + }, + { + RK_PQ_CSC_YUV2YUV_601_709_FULL2LIMIT, "YUV601 F->YUV709 L", + &rk_csc_table_identity_y_cb_cr_601_full_to_y_cb_cr_709_limit, + &rk_dc_csc_table_identity_y_cb_cr_601_full_to_y_cb_cr_709_limit, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_ITU_R_BT_709, true, false + } + }, + { + RK_PQ_CSC_YUV2YUV_709_601_FULL2LIMIT, "YUV709 F->YUV601 L", + &rk_csc_table_identity_y_cb_cr_709_full_to_y_cb_cr_601_limit, + &rk_dc_csc_table_identity_y_cb_cr_709_full_to_y_cb_cr_601_limit, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_XV_YCC_601, true, false + } + }, + { + RK_PQ_CSC_YUV2RGBL_601, "YUV601 L->RGB L", + &rk_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_limit, + &rk_dc_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_limit, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_RGB, false, false + } + }, + { + RK_PQ_CSC_YUV2RGBL_709, "YUV709 L->RGB L", + &rk_csc_table_hdy_cb_cr_limit_to_rgb_limit, + &rk_dc_csc_table_hdy_cb_cr_limit_to_rgb_limit, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_RGB, false, false + } + }, + { + RK_PQ_CSC_RGBL2YUV_601, "RGB L->YUV601 L", + &rk_csc_table_rgb_limit_to_xv_yccsdy_cb_cr, + &rk_dc_csc_table_rgb_limit_to_xv_yccsdy_cb_cr, + { + OPTM_CS_E_RGB, OPTM_CS_E_XV_YCC_601, false, false + } + }, + { + RK_PQ_CSC_RGBL2YUV_709, "RGB L->YUV709 L", + &rk_csc_table_rgb_limit_to_hdy_cb_cr, + &rk_dc_csc_table_rgb_limit_to_hdy_cb_cr, + { + OPTM_CS_E_RGB, OPTM_CS_E_ITU_R_BT_709, false, false + } + }, + { + RK_PQ_CSC_YUV2RGBL_601_FULL, "YUV601 F->RGB L", + &rk_csc_table_xv_yccsdy_cb_cr_to_rgb_limit, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_rgb_limit, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_RGB, true, false + } + }, + { + RK_PQ_CSC_YUV2RGBL_709_FULL, "YUV709 F->RGB L", + &rk_csc_table_hdy_cb_cr_to_rgb_limit, + &rk_dc_csc_table_hdy_cb_cr_to_rgb_limit, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_RGB, true, false + } + }, + { + RK_PQ_CSC_RGBL2YUV_601_FULL, "RGB L->YUV601 F", + &rk_csc_table_rgb_limit_to_xv_yccsdy_cb_cr_full, + &rk_dc_csc_table_rgb_limit_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_RGB, OPTM_CS_E_XV_YCC_601, false, true + } + }, + { + RK_PQ_CSC_RGBL2YUV_709_FULL, "RGB L->YUV709 F", + &rk_csc_table_rgb_limit_to_hdy_cb_cr_full, + &rk_dc_csc_table_rgb_limit_to_hdy_cb_cr_full, + { + OPTM_CS_E_RGB, OPTM_CS_E_ITU_R_BT_709, false, true + } + }, + { + RK_PQ_CSC_RGB2RGBL, "RGB F->RGB L", + &rk_csc_table_identity_rgb_to_rgb_limit, + &rk_dc_csc_table_identity_rgb_to_rgb_limit, + { + OPTM_CS_E_RGB, OPTM_CS_E_RGB, true, false + } + }, + { + RK_PQ_CSC_RGBL2RGB, "RGB L->RGB F", + &rk_csc_table_identity_rgb_limit_to_rgb, + &rk_dc_csc_table_identity_rgb_limit_to_rgb, + { + OPTM_CS_E_RGB, OPTM_CS_E_RGB, false, true + } + }, + { + RK_PQ_CSC_RGBL2RGBL, "RGB L->RGB L", + &rk_csc_table_identity_rgb_to_rgb, + &rk_dc_csc_table_identity_rgb_to_rgb1, + { + OPTM_CS_E_RGB, OPTM_CS_E_RGB, false, false + } + }, + { + RK_PQ_CSC_RGB2RGB, "RGB F->RGB F", + &rk_csc_table_identity_rgb_to_rgb, + &rk_dc_csc_table_identity_rgb_to_rgb2, + { + OPTM_CS_E_RGB, OPTM_CS_E_RGB, true, true + } + }, + { + RK_PQ_CSC_YUV2RGB_2020, "YUV2020 F->RGB2020 F", + &rk_csc_table_identity_yuv_to_rgb_2020, + &rk_dc_csc_table_identity_yuv_to_rgb_2020, + { + OPTM_CS_E_XV_YCC_2020, OPTM_CS_E_RGB_2020, true, true + } + }, + { + RK_PQ_CSC_RGB2YUV2020_LIMIT2FULL, "RGB2020 L->YUV2020 F", + &rk_csc_table_identity_rgb_limit_to_yuv_full_2020, + &rk_dc_csc_table_identity_rgb_limit_to_yuv_full_2020, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_XV_YCC_2020, false, true + } + }, + { + RK_PQ_CSC_RGB2YUV2020_LIMIT, "RGB2020 L->YUV2020 L", + &rk_csc_table_identity_rgb_limit_to_yuv_limit_2020, + &rk_dc_csc_table_identity_rgb_limit_to_yuv_limit_2020, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_XV_YCC_2020, false, false + } + }, + { + RK_PQ_CSC_RGB2YUV2020_FULL2LIMIT, "RGB2020 F->YUV2020 L", + &rk_csc_table_identity_rgb_full_to_yuv_limit_2020, + &rk_dc_csc_table_identity_rgb_full_to_yuv_limit_2020, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_XV_YCC_2020, true, false + } + }, + { + RK_PQ_CSC_RGB2YUV2020_FULL, "RGB2020 F->YUV2020 F", + &rk_csc_table_identity_rgb_full_to_yuv_full_2020, + &rk_dc_csc_table_identity_rgb_full_to_yuv_full_2020, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_XV_YCC_2020, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV, "YUV 601 L->YUV 601 L", + &rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_XV_YCC_601, false, false + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL, "YUV 601 F->YUV 601 F", + &rk_csc_table_identity_y_cb_cr_to_y_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_XV_YCC_601, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_LIMIT2FULL, "YUV 601 L->YUV 601 F", + &rk_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + &rk_dc_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_XV_YCC_601, false, true + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL2LIMIT, "YUV 601 F->YUV 601 L", + &rk_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + &rk_dc_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_XV_YCC_601, true, false + } + }, + { + RK_PQ_CSC_YUV2YUV, "YUV 2020 L->YUV 2020 L", + &rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + { + OPTM_CS_E_XV_YCC_2020, OPTM_CS_E_XV_YCC_2020, false, false + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL, "YUV 2020 F->YUV 2020 F", + &rk_csc_table_identity_y_cb_cr_to_y_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_XV_YCC_2020, OPTM_CS_E_XV_YCC_2020, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_LIMIT2FULL, "YUV 2020 L->YUV 2020 F", + &rk_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + &rk_dc_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + { + OPTM_CS_E_XV_YCC_2020, OPTM_CS_E_XV_YCC_2020, false, true + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL2LIMIT, "YUV 2020 F->YUV 2020 L", + &rk_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + &rk_dc_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + { + OPTM_CS_E_XV_YCC_2020, OPTM_CS_E_XV_YCC_2020, true, false + } + }, + { + RK_PQ_CSC_RGB2RGBL, "RGB 2020 F->RGB 2020 L", + &rk_csc_table_identity_rgb_to_rgb_limit, + &rk_dc_csc_table_identity_rgb_to_rgb_limit, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_RGB_2020, true, false + } + }, + { + RK_PQ_CSC_RGBL2RGB, "RGB 2020 L->RGB 2020 F", + &rk_csc_table_identity_rgb_limit_to_rgb, + &rk_dc_csc_table_identity_rgb_limit_to_rgb, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_RGB_2020, false, true + } + }, + { + RK_PQ_CSC_RGBL2RGBL, "RGB 2020 L->RGB 2020 L", + &rk_csc_table_identity_rgb_to_rgb, + &rk_dc_csc_table_identity_rgb_to_rgb1, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_RGB_2020, false, false + } + }, + { + RK_PQ_CSC_RGB2RGB, "RGB 2020 F->RGB 2020 F", + &rk_csc_table_identity_rgb_to_rgb, + &rk_dc_csc_table_identity_rgb_to_rgb2, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_RGB_2020, true, true + } + }, +}; + +enum vop_csc_format { + CSC_BT601L, + CSC_BT709L, + CSC_BT601F, + CSC_BT2020, + CSC_BT709L_13BIT, + CSC_BT709F_13BIT, + CSC_BT2020L_13BIT, + CSC_BT2020F_13BIT, + CSC_RGBL2BT709F_13BIT, + CSC_RGBL2BT2020F_13BIT, +}; + +enum vop_csc_mode { + CSC_RGB, + CSC_YUV, +}; + +struct csc_mapping { + enum vop_csc_format csc_format; + enum color_space_type rgb_color_space; + enum color_space_type yuv_color_space; + bool rgb_full_range; + bool yuv_full_range; +}; + +static const struct csc_mapping csc_mapping_table[] = { + { + CSC_BT601L, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_601, + true, + false, + }, + { + CSC_BT709L, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_709, + true, + false, + }, + { + CSC_BT601F, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_601, + true, + true, + }, + { + CSC_BT2020, + OPTM_CS_E_RGB_2020, + OPTM_CS_E_XV_YCC_2020, + true, + true, + }, + { + CSC_BT709L_13BIT, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_709, + true, + false, + }, + { + CSC_BT709F_13BIT, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_709, + true, + true, + }, + { + CSC_RGBL2BT709F_13BIT, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_709, + false, + true, + }, + { + CSC_BT2020L_13BIT, + OPTM_CS_E_RGB_2020, + OPTM_CS_E_XV_YCC_2020, + true, + false, + }, + { + CSC_BT2020F_13BIT, + OPTM_CS_E_RGB_2020, + OPTM_CS_E_XV_YCC_2020, + true, + true, + }, + { + CSC_RGBL2BT2020F_13BIT, + OPTM_CS_E_RGB_2020, + OPTM_CS_E_XV_YCC_2020, + false, + true, + }, +}; + +static bool is_rgb_format(u64 format) +{ + switch (format) { + case BUS_FMT_YUV420: + case BUS_FMT_YUV422: + case BUS_FMT_YUV444: + return false; + case BUS_FMT_RGB: + default: + return true; + } +} + +struct post_csc_coef { + s32 csc_coef00; + s32 csc_coef01; + s32 csc_coef02; + s32 csc_coef10; + s32 csc_coef11; + s32 csc_coef12; + s32 csc_coef20; + s32 csc_coef21; + s32 csc_coef22; + + s32 csc_dc0; + s32 csc_dc1; + s32 csc_dc2; + + u32 range_type; +}; + +static int csc_get_mode_index(int post_csc_mode, bool is_input_yuv, bool is_output_yuv) +{ + const struct rk_csc_colorspace_info *colorspace_info; + enum color_space_type input_color_space; + enum color_space_type output_color_space; + bool is_input_full_range; + bool is_output_full_range; + int i; + + for (i = 0; i < ARRAY_SIZE(csc_mapping_table); i++) { + if (post_csc_mode == csc_mapping_table[i].csc_format) { + input_color_space = is_input_yuv ? csc_mapping_table[i].yuv_color_space : + csc_mapping_table[i].rgb_color_space; + is_input_full_range = is_input_yuv ? csc_mapping_table[i].yuv_full_range : + csc_mapping_table[i].rgb_full_range; + output_color_space = is_output_yuv ? csc_mapping_table[i].yuv_color_space : + csc_mapping_table[i].rgb_color_space; + is_output_full_range = is_output_yuv ? csc_mapping_table[i].yuv_full_range : + csc_mapping_table[i].rgb_full_range; + break; + } + } + if (i >= ARRAY_SIZE(csc_mapping_table)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(g_mode_csc_coef); i++) { + colorspace_info = &g_mode_csc_coef[i].st_csc_color_info; + if (colorspace_info->input_color_space == input_color_space && + colorspace_info->output_color_space == output_color_space && + colorspace_info->in_full_range == is_input_full_range && + colorspace_info->out_full_range == is_output_full_range) + return i; + } + + return -EINVAL; +} + +static void __maybe_unused csc_matrix_multiply(struct rk_pq_csc_coef *dst, + const struct rk_pq_csc_coef *m0, + const struct rk_pq_csc_coef *m1) +{ + dst->csc_coef00 = m0->csc_coef00 * m1->csc_coef00 + + m0->csc_coef01 * m1->csc_coef10 + + m0->csc_coef02 * m1->csc_coef20; + + dst->csc_coef01 = m0->csc_coef00 * m1->csc_coef01 + + m0->csc_coef01 * m1->csc_coef11 + + m0->csc_coef02 * m1->csc_coef21; + + dst->csc_coef02 = m0->csc_coef00 * m1->csc_coef02 + + m0->csc_coef01 * m1->csc_coef12 + + m0->csc_coef02 * m1->csc_coef22; + + dst->csc_coef10 = m0->csc_coef10 * m1->csc_coef00 + + m0->csc_coef11 * m1->csc_coef10 + + m0->csc_coef12 * m1->csc_coef20; + + dst->csc_coef11 = m0->csc_coef10 * m1->csc_coef01 + + m0->csc_coef11 * m1->csc_coef11 + + m0->csc_coef12 * m1->csc_coef21; + + dst->csc_coef12 = m0->csc_coef10 * m1->csc_coef02 + + m0->csc_coef11 * m1->csc_coef12 + + m0->csc_coef12 * m1->csc_coef22; + + dst->csc_coef20 = m0->csc_coef20 * m1->csc_coef00 + + m0->csc_coef21 * m1->csc_coef10 + + m0->csc_coef22 * m1->csc_coef20; + + dst->csc_coef21 = m0->csc_coef20 * m1->csc_coef01 + + m0->csc_coef21 * m1->csc_coef11 + + m0->csc_coef22 * m1->csc_coef21; + + dst->csc_coef22 = m0->csc_coef20 * m1->csc_coef02 + + m0->csc_coef21 * m1->csc_coef12 + + m0->csc_coef22 * m1->csc_coef22; +} + +static void csc_matrix_ventor_multiply(struct rk_pq_csc_ventor *dst, + const struct rk_pq_csc_coef *m0, + const struct rk_pq_csc_ventor *v0) +{ + dst->csc_offset0 = m0->csc_coef00 * v0->csc_offset0 + + m0->csc_coef01 * v0->csc_offset1 + + m0->csc_coef02 * v0->csc_offset2; + + dst->csc_offset1 = m0->csc_coef10 * v0->csc_offset0 + + m0->csc_coef11 * v0->csc_offset1 + + m0->csc_coef12 * v0->csc_offset2; + + dst->csc_offset2 = m0->csc_coef20 * v0->csc_offset0 + + m0->csc_coef21 * v0->csc_offset1 + + m0->csc_coef22 * v0->csc_offset2; +} + +static inline s32 pq_csc_simple_round(s32 x, s32 n) +{ + s32 value = 0; + + if (n == 0) + return x; + + value = (abs(x) + (1 << (n - 1))) >> (n); + return (((x) >= 0) ? value : -value); +} + +static void rockchip_swap_color_channel(bool is_input_yuv, bool is_output_yuv, + struct post_csc_coef *csc_simple_coef, + struct rk_pq_csc_coef *out_matrix, + struct rk_pq_csc_ventor *out_dc) +{ + csc_simple_coef->csc_coef00 = out_matrix->csc_coef00; + csc_simple_coef->csc_coef01 = out_matrix->csc_coef01; + csc_simple_coef->csc_coef02 = out_matrix->csc_coef02; + csc_simple_coef->csc_coef10 = out_matrix->csc_coef10; + csc_simple_coef->csc_coef11 = out_matrix->csc_coef11; + csc_simple_coef->csc_coef12 = out_matrix->csc_coef12; + csc_simple_coef->csc_coef20 = out_matrix->csc_coef20; + csc_simple_coef->csc_coef21 = out_matrix->csc_coef21; + csc_simple_coef->csc_coef22 = out_matrix->csc_coef22; + csc_simple_coef->csc_dc0 = out_dc->csc_offset0; + csc_simple_coef->csc_dc1 = out_dc->csc_offset1; + csc_simple_coef->csc_dc2 = out_dc->csc_offset2; +} + +static int csc_calc_default_output_coef(const struct rk_csc_mode_coef *csc_mode_cfg, + struct rk_pq_csc_coef *out_matrix, + struct rk_pq_csc_ventor *out_dc) +{ + const struct rk_pq_csc_coef *csc_coef; + const struct rk_pq_csc_dc_coef *csc_dc_coef; + struct rk_pq_csc_ventor dc_in_ventor; + struct rk_pq_csc_ventor dc_out_ventor; + struct rk_pq_csc_ventor v; + + csc_coef = csc_mode_cfg->pst_csc_coef; + csc_dc_coef = csc_mode_cfg->pst_csc_dc_coef; + + out_matrix->csc_coef00 = csc_coef->csc_coef00; + out_matrix->csc_coef01 = csc_coef->csc_coef01; + out_matrix->csc_coef02 = csc_coef->csc_coef02; + out_matrix->csc_coef10 = csc_coef->csc_coef10; + out_matrix->csc_coef11 = csc_coef->csc_coef11; + out_matrix->csc_coef12 = csc_coef->csc_coef12; + out_matrix->csc_coef20 = csc_coef->csc_coef20; + out_matrix->csc_coef21 = csc_coef->csc_coef21; + out_matrix->csc_coef22 = csc_coef->csc_coef22; + + dc_in_ventor.csc_offset0 = csc_dc_coef->csc_in_dc0; + dc_in_ventor.csc_offset1 = csc_dc_coef->csc_in_dc1; + dc_in_ventor.csc_offset2 = csc_dc_coef->csc_in_dc2; + dc_out_ventor.csc_offset0 = csc_dc_coef->csc_out_dc0; + dc_out_ventor.csc_offset1 = csc_dc_coef->csc_out_dc1; + dc_out_ventor.csc_offset2 = csc_dc_coef->csc_out_dc2; + + csc_matrix_ventor_multiply(&v, csc_coef, &dc_in_ventor); + out_dc->csc_offset0 = v.csc_offset0 + dc_out_ventor.csc_offset0 * + PQ_CSC_SIMPLE_MAT_PARAM_FIX_NUM; + out_dc->csc_offset1 = v.csc_offset1 + dc_out_ventor.csc_offset1 * + PQ_CSC_SIMPLE_MAT_PARAM_FIX_NUM; + out_dc->csc_offset2 = v.csc_offset2 + dc_out_ventor.csc_offset2 * + PQ_CSC_SIMPLE_MAT_PARAM_FIX_NUM; + + return 0; +} + +static int vop2_convert_csc_mode(int csc_mode, int bit_depth, int range) +{ + switch (csc_mode) { + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + return CSC_BT601L; + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SMPTE240M: + case V4L2_COLORSPACE_DEFAULT: + if (bit_depth == CSC_13BIT_DEPTH) + return CSC_BT709L_13BIT; + else + return CSC_BT709L; + case V4L2_COLORSPACE_JPEG: + return CSC_BT601F; + case V4L2_COLORSPACE_BT2020: + if (bit_depth == CSC_13BIT_DEPTH) + return CSC_BT2020L_13BIT; + else + return CSC_BT2020; + case V4L2_COLORSPACE_BT709F: + if (bit_depth == CSC_10BIT_DEPTH) + return CSC_BT601F; + else if (range == CSC_FULL_RANGE) + return CSC_BT709F_13BIT; + else + return CSC_RGBL2BT709F_13BIT; + case V4L2_COLORSPACE_BT2020F: + if (bit_depth == CSC_10BIT_DEPTH) + return CSC_BT601F; + else if (range == CSC_FULL_RANGE) + return CSC_BT2020F_13BIT; + else + return CSC_RGBL2BT2020F_13BIT; + default: + return CSC_BT709L; + } +} + +static int rockchip_calc_post_csc(struct post_csc_coef *csc_simple_coef, + int csc_mode, bool is_input_yuv, bool is_output_yuv) +{ + int ret = 0; + struct rk_pq_csc_coef out_matrix; + struct rk_pq_csc_ventor out_dc; + const struct rk_csc_mode_coef *csc_mode_cfg; + int bit_num = PQ_CSC_SIMPLE_MAT_PARAM_FIX_BIT_WIDTH; + + ret = csc_get_mode_index(csc_mode, is_input_yuv, is_output_yuv); + if (ret < 0) + return ret; + + csc_mode_cfg = &g_mode_csc_coef[ret]; + + + ret = csc_calc_default_output_coef(csc_mode_cfg, &out_matrix, &out_dc); + + rockchip_swap_color_channel(is_input_yuv, is_output_yuv, csc_simple_coef, &out_matrix, + &out_dc); + + csc_simple_coef->csc_dc0 = pq_csc_simple_round(csc_simple_coef->csc_dc0, bit_num); + csc_simple_coef->csc_dc1 = pq_csc_simple_round(csc_simple_coef->csc_dc1, bit_num); + csc_simple_coef->csc_dc2 = pq_csc_simple_round(csc_simple_coef->csc_dc2, bit_num); + csc_simple_coef->range_type = csc_mode_cfg->st_csc_color_info.out_full_range; + + return ret; +} + +static void rk628_post_process_csc(struct rk628 *rk628) +{ + enum bus_format in_fmt, out_fmt; + struct post_csc_coef csc_coef; + bool is_input_yuv, is_output_yuv; + u32 color_space = V4L2_COLORSPACE_BT709F; + u32 csc_mode; + u32 val; + int range_type; + int color_range; + + in_fmt = rk628_hdmirx_get_format(rk628); + out_fmt = rk628->tx_mode ? BUS_FMT_RGB : BUS_FMT_YUV422; + + if (rk628->version == RK628D_VERSION) { + if (in_fmt == BUS_FMT_RGB) + rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_R2Y_EN(1)); + else if (out_fmt == BUS_FMT_RGB) + rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_Y2R_EN(1)); + } else { + color_range = rk628_hdmirx_get_range(rk628); + csc_mode = vop2_convert_csc_mode(color_space, CSC_13BIT_DEPTH, color_range); + + is_input_yuv = !is_rgb_format(in_fmt); + is_output_yuv = !is_rgb_format(out_fmt); + rockchip_calc_post_csc(&csc_coef, csc_mode, is_input_yuv, is_output_yuv); + + val = ((csc_coef.csc_coef01 & 0xffff) << 16) | (csc_coef.csc_coef00 & 0xffff); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_COE01_COE00, val); + + val = ((csc_coef.csc_coef10 & 0xffff) << 16) | (csc_coef.csc_coef02 & 0xffff); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_COE10_COE02, val); + + val = ((csc_coef.csc_coef12 & 0xffff) << 16) | (csc_coef.csc_coef11 & 0xffff); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_COE12_COE11, val); + + val = ((csc_coef.csc_coef21 & 0xffff) << 16) | (csc_coef.csc_coef20 & 0xffff); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_COE21_COE20, val); + + rk628_i2c_write(rk628, GRF_CSC_MATRIX_COE22, csc_coef.csc_coef22); + + rk628_i2c_write(rk628, GRF_CSC_MATRIX_OFFSET0, csc_coef.csc_dc0); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_OFFSET1, csc_coef.csc_dc1); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_OFFSET2, csc_coef.csc_dc2); + + range_type = csc_coef.range_type ? 0 : 1; + range_type <<= is_input_yuv ? 0 : 1; + val = SW_Y2R_MODE(range_type) | SW_FROM_CSC_MATRIX_EN(1); + rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, val); + } +} + +void rk628_post_process_csc_en(struct rk628 *rk628) +{ + rk628_post_process_csc(rk628); + rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_EN(1)); +} +EXPORT_SYMBOL(rk628_post_process_csc_en); + +void rk628_post_process_csc_dis(struct rk628 *rk628) +{ + rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_EN(0)); +} +EXPORT_SYMBOL(rk628_post_process_csc_dis); diff --git a/drivers/media/i2c/rk628/rk628_post_process.h b/drivers/media/i2c/rk628/rk628_post_process.h new file mode 100644 index 000000000000..2b883a56f09e --- /dev/null +++ b/drivers/media/i2c/rk628/rk628_post_process.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Rockchip Electronics Co. Ltd. + * + */ + +#ifndef POST_PROCESS_H +#define POST_PROCESS_H + +void rk628_post_process_csc_en(struct rk628 *rk628); +void rk628_post_process_csc_dis(struct rk628 *rk628); + +#endif diff --git a/drivers/misc/rk628/Makefile b/drivers/misc/rk628/Makefile index cb4285a06f89..fe5b33b4e52f 100644 --- a/drivers/misc/rk628/Makefile +++ b/drivers/misc/rk628/Makefile @@ -3,7 +3,7 @@ rk628_misc-$(CONFIG_RK628_MISC) += rk628.o rk628_cru.o rk628_config.o rk628_post_process.o \ rk628_combrxphy.o rk628_hdmirx.o rk628_combtxphy.o rk628_dsi.o \ panel.o rk628_lvds.o rk628_rgb.o rk628_gvi.o rk628_pinctrl.o \ - rk628_csi.o + rk628_csi.o rk628_efuse.o rk628_misc-$(CONFIG_RK628_MISC_HDMITX) += rk628_hdmitx.o diff --git a/drivers/misc/rk628/panel.c b/drivers/misc/rk628/panel.c index a0a35030779f..64ed0fa4d136 100644 --- a/drivers/misc/rk628/panel.c +++ b/drivers/misc/rk628/panel.c @@ -132,16 +132,16 @@ init_err: int rk628_panel_info_get(struct rk628 *rk628, struct device_node *np) { - struct panel_simple *panel; + struct rk628_panel_simple *panel; struct device *dev = rk628->dev; struct device_node *backlight; int ret; - panel = devm_kzalloc(dev, sizeof(struct panel_simple), GFP_KERNEL); + panel = devm_kzalloc(dev, sizeof(struct rk628_panel_simple), GFP_KERNEL); if (!panel) return -ENOMEM; - panel->supply = devm_regulator_get(dev, "power"); + panel->supply = devm_regulator_get(dev, "panel-power"); if (IS_ERR(panel->supply)) { ret = PTR_ERR(panel->supply); dev_err(dev, "failed to get power regulator: %d\n", ret); @@ -174,9 +174,16 @@ int rk628_panel_info_get(struct rk628 *rk628, struct device_node *np) } + device_property_read_u32(dev, "panel-prepare-delay-ms", &panel->delay.prepare); + device_property_read_u32(dev, "panel-enable-delay-ms", &panel->delay.enable); + device_property_read_u32(dev, "panel-disable-delay-ms", &panel->delay.disable); + device_property_read_u32(dev, "panel-unprepare-delay-ms", &panel->delay.unprepare); + device_property_read_u32(dev, "panel-reset-delay-ms", &panel->delay.reset); + device_property_read_u32(dev, "panel-init-delay-ms", &panel->delay.init); + rk628->panel = panel; - if (rk628->output_mode == OUTPUT_MODE_DSI) { + if (rk628_output_is_dsi(rk628)) { ret = dsi_panel_get_cmds(rk628, np); if (ret) { dev_err(dev, "failed to get cmds\n"); @@ -189,57 +196,75 @@ int rk628_panel_info_get(struct rk628 *rk628, struct device_node *np) void rk628_panel_prepare(struct rk628 *rk628) { + struct rk628_panel_simple *p = rk628->panel; int ret; - if (rk628->panel->supply) { - ret = regulator_enable(rk628->panel->supply); + if (!p) + return; + + if (p->supply) { + ret = regulator_enable(p->supply); if (ret) dev_info(rk628->dev, "failed to enable panel power supply\n"); + } - if (rk628->panel->enable_gpio) { - gpiod_set_value(rk628->panel->enable_gpio, 0); - mdelay(120); - gpiod_set_value(rk628->panel->enable_gpio, 1); - mdelay(120); - } + gpiod_direction_output(p->enable_gpio, 1); + if (p->delay.prepare) + msleep(p->delay.prepare); - if (rk628->panel->reset_gpio) { - gpiod_set_value(rk628->panel->reset_gpio, 0); - mdelay(120); - gpiod_set_value(rk628->panel->reset_gpio, 1); - mdelay(120); - gpiod_set_value(rk628->panel->reset_gpio, 0); - mdelay(120); - } + gpiod_direction_output(p->reset_gpio, 1); + + if (p->delay.reset) + msleep(p->delay.reset); + + gpiod_direction_output(p->reset_gpio, 0); + + if (p->delay.init) + msleep(p->delay.init); } void rk628_panel_enable(struct rk628 *rk628) { - if (rk628->panel->backlight) - backlight_enable(rk628->panel->backlight); + struct rk628_panel_simple *p = rk628->panel; + + if (!p) + return; + + if (p->delay.enable) + msleep(p->delay.enable); + + if (p->backlight) + backlight_enable(p->backlight); } void rk628_panel_unprepare(struct rk628 *rk628) { + struct rk628_panel_simple *p = rk628->panel; - if (rk628->panel->reset_gpio) { - gpiod_set_value(rk628->panel->reset_gpio, 1); - mdelay(120); - } + if (!p) + return; - if (rk628->panel->enable_gpio) { - gpiod_set_value(rk628->panel->enable_gpio, 0); - mdelay(120); - } + gpiod_direction_output(p->reset_gpio, 1); + gpiod_direction_output(p->enable_gpio, 0); if (rk628->panel->supply) regulator_disable(rk628->panel->supply); + + if (p->delay.unprepare) + msleep(p->delay.unprepare); } void rk628_panel_disable(struct rk628 *rk628) { - if (rk628->panel->backlight) - backlight_disable(rk628->panel->backlight); + struct rk628_panel_simple *p = rk628->panel; + if (!p) + return; + + if (p->backlight) + backlight_disable(p->backlight); + + if (p->delay.disable) + msleep(p->delay.disable); } diff --git a/drivers/misc/rk628/rk628.c b/drivers/misc/rk628/rk628.c index 15df730c8048..4cbba8aa6cda 100644 --- a/drivers/misc/rk628/rk628.c +++ b/drivers/misc/rk628/rk628.c @@ -29,10 +29,12 @@ #include "rk628_gvi.h" #include "rk628_csi.h" #include "rk628_hdmitx.h" +#include "rk628_efuse.h" static const struct regmap_range rk628_cru_readable_ranges[] = { regmap_reg_range(CRU_CPLL_CON0, CRU_CPLL_CON4), regmap_reg_range(CRU_GPLL_CON0, CRU_GPLL_CON4), + regmap_reg_range(CRU_APLL_CON0, CRU_APLL_CON4), regmap_reg_range(CRU_MODE_CON00, CRU_MODE_CON00), regmap_reg_range(CRU_CLKSEL_CON00, CRU_CLKSEL_CON21), regmap_reg_range(CRU_GATE_CON00, CRU_GATE_CON05), @@ -62,6 +64,7 @@ static const struct regmap_range rk628_hdmirx_readable_ranges[] = { regmap_reg_range(HDMI_RX_HDMI_MODE_RECOVER, HDMI_RX_HDMI_ERROR_PROTECT), regmap_reg_range(HDMI_RX_HDMI_SYNC_CTRL, HDMI_RX_HDMI_CKM_RESULT), regmap_reg_range(HDMI_RX_HDMI_RESMPL_CTRL, HDMI_RX_HDMI_RESMPL_CTRL), + regmap_reg_range(HDMI_RX_HDMI_DCM_CTRL, HDMI_RX_HDMI_DCM_CTRL), regmap_reg_range(HDMI_RX_HDMI_VM_CFG_CH2, HDMI_RX_HDMI_STS), regmap_reg_range(HDMI_RX_HDCP_CTRL, HDMI_RX_HDCP_SETTINGS), regmap_reg_range(HDMI_RX_HDCP_KIDX, HDMI_RX_HDCP_KIDX), @@ -78,6 +81,7 @@ static const struct regmap_range rk628_hdmirx_readable_ranges[] = { regmap_reg_range(HDMI_RX_AUD_CHEXTR_CTRL, HDMI_RX_AUD_PAO_CTRL), regmap_reg_range(HDMI_RX_AUD_FIFO_STS, HDMI_RX_AUD_FIFO_STS), regmap_reg_range(HDMI_RX_AUDPLL_GEN_CTS, HDMI_RX_AUDPLL_GEN_N), + regmap_reg_range(HDMI_RX_I2CM_PHYG3_DATAI, HDMI_RX_I2CM_PHYG3_DATAI), regmap_reg_range(HDMI_RX_PDEC_CTRL, HDMI_RX_PDEC_CTRL), regmap_reg_range(HDMI_RX_PDEC_AUDIODET_CTRL, HDMI_RX_PDEC_AUDIODET_CTRL), regmap_reg_range(HDMI_RX_PDEC_ERR_FILTER, HDMI_RX_PDEC_ASP_CTRL), @@ -87,7 +91,7 @@ static const struct regmap_range rk628_hdmirx_readable_ranges[] = { regmap_reg_range(HDMI_RX_PDEC_AIF_CTRL, HDMI_RX_PDEC_AIF_PB0), regmap_reg_range(HDMI_RX_PDEC_AVI_PB, HDMI_RX_PDEC_AVI_PB), regmap_reg_range(HDMI_RX_HDMI20_CONTROL, HDMI_RX_CHLOCK_CONFIG), - regmap_reg_range(HDMI_RX_SCDC_REGS1, HDMI_RX_SCDC_REGS2), + regmap_reg_range(HDMI_RX_SCDC_REGS0, HDMI_RX_SCDC_REGS2), regmap_reg_range(HDMI_RX_SCDC_WRDATA0, HDMI_RX_SCDC_WRDATA0), regmap_reg_range(HDMI_RX_PDEC_ISTS, HDMI_RX_PDEC_IEN), regmap_reg_range(HDMI_RX_AUD_FIFO_ISTS, HDMI_RX_AUD_FIFO_IEN), @@ -110,6 +114,15 @@ static const struct regmap_access_table rk628_key_readable_table = { .n_yes_ranges = ARRAY_SIZE(rk628_key_readable_ranges), }; +static const struct regmap_range rk628_efuse_readable_ranges[] = { + regmap_reg_range(EFUSE_BASE, EFUSE_BASE + EFUSE_REVISION), +}; + +static const struct regmap_access_table rk628_efuse_readable_table = { + .yes_ranges = rk628_efuse_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(rk628_efuse_readable_ranges), +}; + static const struct regmap_range rk628_combtxphy_readable_ranges[] = { regmap_reg_range(COMBTXPHY_BASE, COMBTXPHY_CON10), }; @@ -138,7 +151,7 @@ static const struct regmap_access_table rk628_dsi1_readable_table = { }; static const struct regmap_range rk628_gvi_readable_ranges[] = { - regmap_reg_range(GVI_BASE, GVI_BASE + GVI_COLOR_BAR_VTIMING1), + regmap_reg_range(GVI_BASE, GVI_COLOR_BAR_VTIMING1), }; static const struct regmap_access_table rk628_gvi_readable_table = { @@ -255,6 +268,16 @@ static const struct regmap_config rk628_regmap_config[RK628_DEV_MAX] = { .val_format_endian = REGMAP_ENDIAN_NATIVE, .rd_table = &rk628_key_readable_table, }, + [RK628_DEV_EFUSE] = { + .name = "efuse", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = EFUSE_BASE + EFUSE_REVISION, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .rd_table = &rk628_efuse_readable_table, + }, [RK628_DEV_COMBTXPHY] = { .name = "combtxphy", .reg_bits = 32, @@ -357,77 +380,56 @@ static const struct regmap_config rk628_regmap_config[RK628_DEV_MAX] = { }, }; +static void rk628_power_on(struct rk628 *rk628, bool on) +{ + if (!rk628->display_enabled && on) { + gpiod_set_value(rk628->enable_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value(rk628->reset_gpio, 0); + usleep_range(10000, 11000); + gpiod_set_value(rk628->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value(rk628->reset_gpio, 0); + usleep_range(10000, 11000); + } + + if (!on) { + gpiod_set_value(rk628->reset_gpio, 1); + gpiod_set_value(rk628->enable_gpio, 0); + } +} + static void rk628_display_disable(struct rk628 *rk628) { if (!rk628->display_enabled) return; - if (rk628->output_mode == OUTPUT_MODE_CSI) + if (rk628_output_is_csi(rk628)) rk628_csi_disable(rk628); - if (rk628->output_mode == OUTPUT_MODE_GVI) + if (rk628_output_is_gvi(rk628)) rk628_gvi_disable(rk628); - if (rk628->output_mode == OUTPUT_MODE_LVDS) + if (rk628_output_is_lvds(rk628)) rk628_lvds_disable(rk628); - if (rk628->output_mode == OUTPUT_MODE_DSI) + if (rk628_output_is_dsi(rk628)) rk628_dsi_disable(rk628); + if (rk628_output_is_rgb(rk628)) + rk628_rgb_tx_disable(rk628); + rk628_post_process_disable(rk628); - if (rk628->input_mode == INPUT_MODE_HDMI) + if (rk628_input_is_hdmi(rk628)) rk628_hdmirx_disable(rk628); + if (rk628_output_is_hdmi(rk628)) + rk628_hdmitx_disable(rk628); + rk628->display_enabled = false; } -static void rk628_display_resume(struct rk628 *rk628) -{ - u8 ret = 0; - - if (rk628->display_enabled) - return; - - if (rk628->input_mode == INPUT_MODE_HDMI) { - ret = rk628_hdmirx_enable(rk628); - if ((ret == HDMIRX_PLUGOUT) || (ret & HDMIRX_NOSIGNAL)) { - rk628_display_disable(rk628); - return; - } - } - - if (rk628->input_mode == INPUT_MODE_RGB) - rk628_rgb_rx_enable(rk628); - - if (rk628->input_mode == INPUT_MODE_BT1120) - rk628_bt1120_rx_enable(rk628); - - rk628_post_process_init(rk628); - rk628_post_process_enable(rk628); - - if (rk628->output_mode == OUTPUT_MODE_DSI) { - rk628_mipi_dsi_pre_enable(rk628); - rk628_mipi_dsi_enable(rk628); - } - - if (rk628->output_mode == OUTPUT_MODE_LVDS) - rk628_lvds_enable(rk628); - - if (rk628->output_mode == OUTPUT_MODE_GVI) - rk628_gvi_enable(rk628); - - if (rk628->output_mode == OUTPUT_MODE_CSI) - rk628_csi_enable(rk628); - -#ifdef CONFIG_RK628_MISC_HDMITX - if (rk628->output_mode == OUTPUT_MODE_HDMI) - rk628_hdmitx_enable(rk628); -#endif - - rk628->display_enabled = true; -} - static void rk628_display_enable(struct rk628 *rk628) { u8 ret = 0; @@ -435,19 +437,19 @@ static void rk628_display_enable(struct rk628 *rk628) if (rk628->display_enabled) return; - if (rk628->input_mode == INPUT_MODE_RGB) + if (rk628_input_is_rgb(rk628)) rk628_rgb_rx_enable(rk628); - if (rk628->input_mode == INPUT_MODE_BT1120) + if (rk628_input_is_bt1120(rk628)) rk628_bt1120_rx_enable(rk628); - if (rk628->output_mode == OUTPUT_MODE_BT1120) - rk628_bt1120_tx_enable(rk628); + if (rk628_output_is_rgb(rk628)) + rk628_rgb_tx_enable(rk628); - if (rk628->output_mode == OUTPUT_MODE_DSI) + if (rk628_output_is_dsi(rk628)) queue_delayed_work(rk628->dsi_wq, &rk628->dsi_delay_work, msecs_to_jiffies(10)); - if (rk628->input_mode == INPUT_MODE_HDMI) { + if (rk628_input_is_hdmi(rk628)) { ret = rk628_hdmirx_enable(rk628); if ((ret == HDMIRX_PLUGOUT) || (ret & HDMIRX_NOSIGNAL)) { rk628_display_disable(rk628); @@ -455,28 +457,127 @@ static void rk628_display_enable(struct rk628 *rk628) } } - if (rk628->output_mode != OUTPUT_MODE_HDMI) { + if (rk628_output_is_bt1120(rk628)) + rk628_bt1120_tx_enable(rk628); + + if (!rk628_output_is_hdmi(rk628)) { rk628_post_process_init(rk628); rk628_post_process_enable(rk628); } - if (rk628->output_mode == OUTPUT_MODE_LVDS) + if (rk628_output_is_lvds(rk628)) rk628_lvds_enable(rk628); - if (rk628->output_mode == OUTPUT_MODE_GVI) + if (rk628_output_is_gvi(rk628)) rk628_gvi_enable(rk628); - if (rk628->output_mode == OUTPUT_MODE_CSI) + if (rk628_output_is_csi(rk628)) rk628_csi_enable(rk628); -#ifdef CONFIG_RK628_MISC_HDMITX - if (rk628->output_mode == OUTPUT_MODE_HDMI) + if (rk628_output_is_hdmi(rk628)) rk628_hdmitx_enable(rk628); -#endif rk628->display_enabled = true; } +static void rk628_set_hdmirx_irq(struct rk628 *rk628, u32 reg, bool enable) +{ + if (rk628->version != RK628D_VERSION) + rk628_i2c_write(rk628, reg, RK628F_HDMIRX_IRQ_EN(enable)); + else + rk628_i2c_write(rk628, reg, RK628D_HDMIRX_IRQ_EN(enable)); +} + +static void rk628_pwr_consumption_init(struct rk628 *rk628) +{ + /* set pin as int function to allow output interrupt */ + rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x30002000); + + /* + * set unuse pin as GPIO input and + * pull down to reduce power consumption + */ + rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffff0000); + rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff0000); + rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b0000); + rk628_i2c_write(rk628, GRF_GPIO2C_P_CON, 0x3f0015); + rk628_i2c_write(rk628, GRF_GPIO3A_P_CON, 0xcc0044); + + if (!rk628_output_is_hdmi(rk628)) { + u32 mask = SW_OUTPUT_MODE_MASK; + u32 val = SW_OUTPUT_MODE(OUTPUT_MODE_HDMI); + + if (rk628->version == RK628F_VERSION) { + mask = SW_HDMITX_EN_MASK; + val = SW_HDMITX_EN(1); + } + + /* disable clock/data channel and band gap when hdmitx not work */ + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, mask, val); + rk628_i2c_write(rk628, HDMI_PHY_SYS_CTL, 0x17); + rk628_i2c_write(rk628, HDMI_PHY_CHG_PWR, 0x0); + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, mask, 0); + } +} + +#ifdef CONFIG_FB +static int rk628_fb_notifier_callback(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct rk628 *rk628 = container_of(nb, struct rk628, fb_nb); + struct fb_event *ev_data = data; + int *blank; + + if ((event != FB_EVENT_BLANK) || (!ev_data) || (!ev_data->data)) + return NOTIFY_DONE; + + blank = ev_data->data; + if (rk628->old_blank == *blank) + return NOTIFY_DONE; + + rk628->old_blank = *blank; + + switch (*blank) { + case FB_BLANK_UNBLANK: + rk628_power_on(rk628, true); + rk628_pwr_consumption_init(rk628); + rk628_cru_init(rk628); + + if (rk628_input_is_hdmi(rk628)) { + rk628_set_hdmirx_irq(rk628, GRF_INTR0_EN, true); + + /* + * make hdmi rx register domain polling + * access to be normal by setting hdmi in + */ + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, + SW_INPUT_MODE_MASK, + SW_INPUT_MODE(INPUT_MODE_HDMI)); + + queue_delayed_work(rk628->monitor_wq, + &rk628->delay_work, + msecs_to_jiffies(50)); + + return NOTIFY_OK; + } + + rk628_display_enable(rk628); + + return NOTIFY_OK; + case FB_BLANK_POWERDOWN: + if (rk628_input_is_hdmi(rk628)) + cancel_delayed_work_sync(&rk628->delay_work); + + rk628_display_disable(rk628); + rk628_power_on(rk628, false); + + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} +#endif + static void rk628_display_work(struct work_struct *work) { u8 ret = 0; @@ -484,8 +585,9 @@ static void rk628_display_work(struct work_struct *work) container_of(work, struct rk628, delay_work.work); int delay = msecs_to_jiffies(2000); - if (rk628->input_mode == INPUT_MODE_HDMI) { + if (rk628_input_is_hdmi(rk628)) { ret = rk628_hdmirx_detect(rk628); + dev_info(rk628->dev, "%s: hdmirx detect status:0x%x\n", __func__, ret); if (!(ret & (HDMIRX_CHANGED | HDMIRX_NOLOCK))) { if (!rk628->plugin_det_gpio) queue_delayed_work(rk628->monitor_wq, @@ -504,7 +606,7 @@ static void rk628_display_work(struct work_struct *work) rk628_display_disable(rk628); } - if (rk628->input_mode == INPUT_MODE_HDMI) { + if (rk628_input_is_hdmi(rk628)) { if (!rk628->plugin_det_gpio) { if (ret & HDMIRX_NOLOCK) delay = msecs_to_jiffies(200); @@ -527,12 +629,15 @@ static void rk628_dsi_work(struct work_struct *work) static irqreturn_t rk628_hdmirx_plugin_irq(int irq, void *dev_id) { struct rk628 *rk628 = dev_id; + u32 val; rk628_hdmirx_enable_interrupts(rk628, false); - /* clear interrupts */ - rk628_i2c_write(rk628, HDMI_RX_MD_ICLR, 0xffffffff); - rk628_i2c_write(rk628, HDMI_RX_PDEC_ICLR, 0xffffffff); - rk628_i2c_write(rk628, GRF_INTR0_CLR_EN, 0x01000100); + /* update hdmirx phy lock status */ + rk628_hdmirx_detect(rk628); + rk628_i2c_read(rk628, HDMI_RX_MD_ISTS, &val); + dev_dbg(rk628->dev, "HDMI_RX_MD_ISTS:0x%x\n", val); + rk628_i2c_write(rk628, HDMI_RX_MD_ICLR, val); + rk628_set_hdmirx_irq(rk628, GRF_INTR0_CLR_EN, true); /* control hpd after 50ms */ schedule_delayed_work(&rk628->delay_work, HZ / 20); @@ -540,12 +645,76 @@ static irqreturn_t rk628_hdmirx_plugin_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static bool rk628_input_is_rgb(struct rk628 *rk628) +static bool rk628_display_route_check(struct rk628 *rk628) { - if (rk628->input_mode == INPUT_MODE_RGB || rk628->input_mode == INPUT_MODE_BT1120) + if (!hweight32(rk628->input_mode) || !hweight32(rk628->output_mode)) + return false; + + /* + * the RGB/BT1120 RX and RGB/BT1120 TX are the same shared IP + * and cannot be used as both input and output simultaneously. + */ + if ((rk628_input_is_rgb(rk628) || rk628_input_is_bt1120(rk628)) && + (rk628_output_is_rgb(rk628) || rk628_output_is_bt1120(rk628))) + return false; + + if (rk628->version == RK628F_VERSION) return true; - return false; + /* rk628d only support rgb and hdmi output simultaneously */ + if (hweight32(rk628->output_mode) > 2) + return false; + + if (hweight32(rk628->output_mode) == 2 && + !(rk628_output_is_rgb(rk628) && rk628_output_is_hdmi(rk628))) + return false; + + return true; +} + +static void rk628_final_display_route(struct rk628 *rk628) +{ + char *input = NULL; + char *output = NULL; + char buf[20]; + + if (rk628_input_is_hdmi(rk628)) + input = "hdmi"; + else if (rk628_input_is_bt1120(rk628)) + input = "bt1120"; + else + input = "rgb"; + + if (rk628_output_is_gvi(rk628)) + output = "gvi"; + else if (rk628_output_is_lvds(rk628)) + output = "lvds"; + else if (rk628_output_is_dsi(rk628)) + output = "dsi"; + else if (rk628_output_is_csi(rk628)) + output = "csi"; + + if (rk628_output_is_bt1120(rk628)) { + if (output) + sprintf(buf, "%s & %s", output, "bt1120"); + else + output = "bt1120"; + } else if (rk628_output_is_rgb(rk628)) { + if (output) + sprintf(buf, "%s & %s", output, "rgb"); + else + output = "rgb"; + } + + if (rk628_output_is_hdmi(rk628)) { + if (output) + sprintf(buf, "%s & %s", output, "hdmi"); + else + output = "hdmi"; + } + + dev_info(rk628->dev, "input_mode:%s output_mode:%s\n", input, + hweight32(rk628->output_mode) > 1 ? buf : output); } static int rk628_display_route_info_parse(struct rk628 *rk628) @@ -554,31 +723,42 @@ static int rk628_display_route_info_parse(struct rk628 *rk628) int ret = 0; u32 val; - if (of_property_read_bool(rk628->dev->of_node, "rk628,hdmi-in")) - rk628->input_mode = INPUT_MODE_HDMI; - else if (of_property_read_bool(rk628->dev->of_node, "rk628,rgb-in")) - rk628->input_mode = INPUT_MODE_RGB; - else if (of_property_read_bool(rk628->dev->of_node, "rk628,bt1120-in")) - rk628->input_mode = INPUT_MODE_BT1120; - else - rk628->input_mode = INPUT_MODE_RGB; - - if (of_find_node_by_name(rk628->dev->of_node, "rk628-dsi")) { - np = of_find_node_by_name(rk628->dev->of_node, "rk628-dsi"); - ret = rk628_dsi_parse(rk628, np); - } else if (of_find_node_by_name(rk628->dev->of_node, "rk628-lvds")) { - np = of_find_node_by_name(rk628->dev->of_node, "rk628-lvds"); - ret = rk628_lvds_parse(rk628, np); - } else if (of_find_node_by_name(rk628->dev->of_node, "rk628-gvi")) { - np = of_find_node_by_name(rk628->dev->of_node, "rk628-gvi"); - ret = rk628_gvi_parse(rk628, np); - } else if (of_find_node_by_name(rk628->dev->of_node, "rk628-bt1120")) { - rk628->output_mode = OUTPUT_MODE_BT1120; + if (of_property_read_bool(rk628->dev->of_node, "rk628,hdmi-in")) { + rk628->input_mode = BIT(INPUT_MODE_HDMI); + } else if (of_property_read_bool(rk628->dev->of_node, "rk628,rgb-in")) { + rk628->input_mode = BIT(INPUT_MODE_RGB); + ret = rk628_rgb_parse(rk628, NULL); + } else if (of_property_read_bool(rk628->dev->of_node, "rk628,bt1120-in")) { + rk628->input_mode = BIT(INPUT_MODE_BT1120); + ret = rk628_rgb_parse(rk628, NULL); } else { - if (of_property_read_bool(rk628->dev->of_node, "rk628,hdmi-out")) - rk628->output_mode = OUTPUT_MODE_HDMI; - else if (of_property_read_bool(rk628->dev->of_node, "rk628,csi-out")) - rk628->output_mode = OUTPUT_MODE_CSI; + rk628->input_mode = BIT(INPUT_MODE_RGB); + } + + if ((np = of_get_child_by_name(rk628->dev->of_node, "rk628-gvi"))) { + rk628->output_mode |= BIT(OUTPUT_MODE_GVI); + ret = rk628_gvi_parse(rk628, np); + } else if ((np = of_get_child_by_name(rk628->dev->of_node, "rk628-lvds"))) { + rk628->output_mode |= BIT(OUTPUT_MODE_LVDS); + ret = rk628_lvds_parse(rk628, np); + } else if ((np = of_get_child_by_name(rk628->dev->of_node, "rk628-dsi"))) { + rk628->output_mode |= BIT(OUTPUT_MODE_DSI); + ret = rk628_dsi_parse(rk628, np); + } else if (of_property_read_bool(rk628->dev->of_node, "rk628,csi-out")) { + rk628->output_mode |= BIT(OUTPUT_MODE_CSI); + } + if (np) + of_node_put(np); + + if (of_property_read_bool(rk628->dev->of_node, "rk628,hdmi-out")) + rk628->output_mode |= BIT(OUTPUT_MODE_HDMI); + + if (of_property_read_bool(rk628->dev->of_node, "rk628-rgb")) { + rk628->output_mode |= BIT(OUTPUT_MODE_RGB); + ret = rk628_rgb_parse(rk628, NULL); + } else if (of_property_read_bool(rk628->dev->of_node, "rk628-bt1120")) { + rk628->output_mode |= BIT(OUTPUT_MODE_BT1120); + ret = rk628_rgb_parse(rk628, NULL); } if (of_property_read_u32(rk628->dev->of_node, "mode-sync-pol", &val) < 0) @@ -586,9 +766,6 @@ static int rk628_display_route_info_parse(struct rk628 *rk628) else rk628->sync_pol = (!val ? MODE_FLAG_NSYNC : MODE_FLAG_PSYNC); - if (rk628_input_is_rgb(rk628) && rk628->output_mode == OUTPUT_MODE_RGB) - return -EINVAL; - return ret; } @@ -613,7 +790,7 @@ rk628_display_mode_from_videomode(const struct rk628_videomode *vm, static void of_parse_rk628_display_timing(struct device_node *np, struct rk628_videomode *vm) { - u8 val; + u32 val; of_property_read_u32(np, "clock-frequency", &vm->pixelclock); of_property_read_u32(np, "hactive", &vm->hactive); @@ -627,10 +804,10 @@ of_parse_rk628_display_timing(struct device_node *np, struct rk628_videomode *vm of_property_read_u32(np, "vsync-len", &vm->vsync_len); vm->flags = 0; - of_property_read_u8(np, "hsync-active", &val); + of_property_read_u32(np, "hsync-active", &val); vm->flags |= val ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; - of_property_read_u8(np, "vsync-active", &val); + of_property_read_u32(np, "vsync-active", &val); vm->flags |= val ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; } @@ -702,253 +879,168 @@ static int rk628_display_timings_get(struct rk628 *rk628) pr_info(args); \ } while (0) -static int rk628_debugfs_dump(struct seq_file *s, void *data) +static void rk628_show_input_mode(struct seq_file *s) +{ + struct rk628 *rk628 = s->private; + char input_s[10] = {0}; + + /* show input mode */ + if (rk628_input_is_hdmi(rk628)) + strcpy(input_s, "HDMI"); + else if (rk628_input_is_bt1120(rk628)) + strcpy(input_s, "BT1120"); + else if (rk628_input_is_rgb(rk628)) + strcpy(input_s, "RGB"); + + if (!strlen(input_s)) + strcpy(input_s, "unknown "); + + DEBUG_PRINT("input: %s\n", input_s); +} + +static void rk628_show_resolution(struct seq_file *s) +{ + struct rk628 *rk628 = s->private; + struct rk628_display_mode *src_mode = &rk628->src_mode; + u32 src_hactive, src_hs_start, src_hs_end, src_htotal; + u32 src_vactive, src_vs_start, src_vs_end, src_vtotal; + u32 clk_rx_read; + u32 fps; + + /* get timing */ + clk_rx_read = src_mode->clock; + src_hactive = src_mode->hdisplay; + src_hs_start = src_mode->hsync_start; + src_hs_end = src_mode->hsync_end; + src_htotal = src_mode->htotal; + src_vactive = src_mode->vdisplay; + src_vs_start = src_mode->vsync_start; + src_vs_end = src_mode->vsync_end; + src_vtotal = src_mode->vtotal; + + /* get fps */ + fps = clk_rx_read * 1000; + do_div(fps, src_htotal * src_vtotal); + + /* print */ + DEBUG_PRINT(" Display mode: %dx%dp%d,dclk[%u]\n", src_hactive, + src_vactive, fps, clk_rx_read); + DEBUG_PRINT("\tH: %d %d %d %d\n", src_hactive, src_hs_start, src_hs_end, + src_htotal); + DEBUG_PRINT("\tV: %d %d %d %d\n", src_vactive, src_vs_start, src_vs_end, + src_vtotal); +} + +static void rk628f_show_rgbrx_resolution(struct seq_file *s) +{ + struct rk628 *rk628 = s->private; + u32 val; + + u64 clk_rx_read; + u64 imodet_clk; + u32 rgb_rx_eval_time, rgb_rx_clkrate; + + u16 src_hactive = 0, src_vactive = 0; + u16 src_htotal, src_vtotal; + u32 fps; + + /* get clk rgbrx read */ + rk628_i2c_read(rk628, GRF_RGB_RX_DBG_MEAS0, &val); + rgb_rx_eval_time = (val & RGB_RX_EVAL_TIME_MASK) >> 16; + + rk628_i2c_read(rk628, GRF_RGB_RX_DBG_MEAS2, &val); + rgb_rx_clkrate = val & RGB_RX_CLKRATE_MASK; + + imodet_clk = rk628_cru_clk_get_rate(rk628, CGU_CLK_IMODET); + + clk_rx_read = imodet_clk * rgb_rx_clkrate / (rgb_rx_eval_time + 1); + do_div(clk_rx_read, 1000); + + /* get timing */ + if (rk628_input_is_rgb(rk628)) { + rk628_i2c_read(rk628, GRF_RGB_RX_DBG_MEAS4, &val); + src_vactive = (val >> 16) & 0xffff; + src_hactive = val & 0xffff; + } else if (rk628_input_is_bt1120(rk628)) { + rk628_i2c_read(rk628, GRF_SYSTEM_STATUS3, &val); + src_vactive = val & DECODER_1120_LAST_LINE_NUM_MASK; + + rk628_i2c_read(rk628, GRF_SYSTEM_STATUS4, &val); + src_hactive = val & DECODER_1120_LAST_PIX_NUM_MASK; + } + + rk628_i2c_read(rk628, 0x134, &val); + src_htotal = (val & 0xffff0000) >> 16; + src_vtotal = val & 0xffff; + + /* get fps */ + fps = clk_rx_read * 1000; + do_div(fps, src_htotal * src_vtotal); + + /* print */ + DEBUG_PRINT(" Display mode: %dx%dp%d,dclk[%llu]\n", src_hactive, + src_vactive, fps, clk_rx_read); + DEBUG_PRINT("\tH-total: %d\n", src_htotal); + DEBUG_PRINT("\tV-total: %d\n", src_vtotal); +} + +static void rk628_show_input_resolution(struct seq_file *s) { struct rk628 *rk628 = s->private; + if ((rk628_input_is_rgb(rk628) || rk628_input_is_bt1120(rk628)) && + rk628->version == RK628F_VERSION) + rk628f_show_rgbrx_resolution(s); + else + rk628_show_resolution(s); +} + +static void rk628_show_output_mode(struct seq_file *s) +{ + struct rk628 *rk628 = s->private; + char output_s[30] = {0}; + char *p = output_s; + + if (rk628_output_is_gvi(rk628)) + strcpy(p, "GVI "); + else if (rk628_output_is_lvds(rk628)) + strcpy(p, "LVDS "); + else if (rk628_output_is_dsi(rk628)) + strcpy(p, "DSI "); + else if (rk628_output_is_csi(rk628)) + strcpy(p, "CSI "); + p += strlen(p); + + if (rk628_output_is_hdmi(rk628)) { + strcpy(p, "HDMI "); + p += strlen(p); + } + + if (rk628_output_is_rgb(rk628)) + strcpy(p, "RGB "); + else if (rk628_output_is_bt1120(rk628)) + strcpy(p, "BT1120 "); + + if (!strlen(output_s)) + strcpy(p, "unknown "); + + DEBUG_PRINT("output: %s\n", output_s); +} + +static void rk628_show_output_resolution(struct seq_file *s) +{ + struct rk628 *rk628 = s->private; u32 val; + u64 sclk_vop; u32 dsp_htotal, dsp_hs_end, dsp_hact_st, dsp_hact_end; u32 dsp_vtotal, dsp_vs_end, dsp_vact_st, dsp_vact_end; - u32 src_hactive, src_hoffset, src_htotal, src_hs_end; - u32 src_vactive, src_voffset, src_vtotal, src_vs_end; - - u32 input_mode, output_mode; - char input_s[10]; - char output_s[13]; - - bool r2y, y2r; - char csc_mode_r2y_s[10]; - char csc_mode_y2r_s[10]; - u32 csc; - enum csc_mode { - BT601_L, - BT709_L, - BT601_F, - BT2020 - }; - - int sw_hsync_pol, sw_vsync_pol; - u32 dsp_frame_v_start, dsp_frame_h_start; - - int sclk_vop_sel = 0; - u32 sclk_vop_div; - u64 sclk_vop; - u32 reg_v; u32 fps; - u32 imodet_clk; - u32 imodet_clk_sel; - u32 imodet_clk_div; + /* get sclk_vop */ + sclk_vop = rk628_cru_clk_get_rate(rk628, CGU_SCLK_VOP); + do_div(sclk_vop, 1000); - int clk_rx_read_sel = 0; - u32 clk_rx_read_div; - u64 clk_rx_read; - - u32 tdms_clk_div; - u32 tdms_clk; - u32 common_tdms_clk[19] = { - 25170, 27000, 33750, 40000, 59400, - 65000, 68250, 74250, 83500, 85500, - 88750, 92812, 101000, 108000, 119000, - 135000, 148500, 162000, 297000, - }; - - //get sclk vop - rk628_i2c_read(rk628, 0xc0088, ®_v); - sclk_vop_sel = (reg_v & 0x20) ? 1 : 0; - rk628_i2c_read(rk628, 0xc00b4, ®_v); - if (reg_v) - sclk_vop_div = reg_v; - else - sclk_vop_div = 0x10002; - /* gpll 983.04MHz */ - /* cpll 1188MHz */ - if (sclk_vop_sel) - sclk_vop = (u64)983040 * ((sclk_vop_div & 0xffff0000) >> 16); - else - sclk_vop = (u64)1188000 * ((sclk_vop_div & 0xffff0000) >> 16); - do_div(sclk_vop, sclk_vop_div & 0xffff); - - //get rx read clk - rk628_i2c_read(rk628, 0xc0088, ®_v); - clk_rx_read_sel = (reg_v & 0x10) ? 1 : 0; - rk628_i2c_read(rk628, 0xc00b8, ®_v); - if (reg_v) - clk_rx_read_div = reg_v; - else - clk_rx_read_div = 0x10002; - /* gpll 983.04MHz */ - /* cpll 1188MHz */ - if (clk_rx_read_sel) - clk_rx_read = (u64)983040 * ((clk_rx_read_div & 0xffff0000) >> 16); - else - clk_rx_read = (u64)1188000 * ((clk_rx_read_div & 0xffff0000) >> 16); - do_div(clk_rx_read, clk_rx_read_div & 0xffff); - - //get imodet clk - rk628_i2c_read(rk628, 0xc0094, ®_v); - imodet_clk_sel = (reg_v & 0x20) ? 1 : 0; - - if (reg_v) - imodet_clk_div = (reg_v & 0x1f) + 1; - else - imodet_clk_div = 0x18; - /* gpll 983.04MHz */ - /* cpll 1188MHz */ - if (imodet_clk_sel) - imodet_clk = 983040 / imodet_clk_div; - else - imodet_clk = 1188000 / imodet_clk_div; - - //get input interface type - rk628_i2c_read(rk628, GRF_SYSTEM_CON0, &val); - input_mode = val & 0x7; - output_mode = (val & 0xf8) >> 3; - sw_hsync_pol = (val & 0x4000000) ? 1 : 0; - sw_vsync_pol = (val & 0x2000000) ? 1 : 0; - switch (input_mode) { - case 0: - strcpy(input_s, "HDMI"); - break; - case 1: - strcpy(input_s, "reserved"); - break; - case 2: - strcpy(input_s, "BT1120"); - break; - case 3: - strcpy(input_s, "RGB"); - break; - case 4: - strcpy(input_s, "YUV"); - break; - default: - strcpy(input_s, "unknown"); - } - DEBUG_PRINT("input:%s\n", input_s); - if (input_mode == 0) { - //get tdms clk - rk628_i2c_read(rk628, 0x16654, ®_v); - reg_v = (reg_v & 0x3f0000) >> 16; - if (reg_v >= 0 && reg_v <= 19) - tdms_clk = common_tdms_clk[reg_v]; - else - tdms_clk = 148500; - - rk628_i2c_read(rk628, 0x166a8, ®_v); - reg_v = (reg_v & 0xf00) >> 8; - if (reg_v == 0x6) - tdms_clk_div = 1; - else if (reg_v == 0x0) - tdms_clk_div = 2; - else - tdms_clk_div = 1; - - //get input hdmi timing - //get horizon timing - rk628_i2c_read(rk628, 0x30150, ®_v); - src_hactive = reg_v & 0xffff; - - rk628_i2c_read(rk628, 0x3014c, ®_v); - src_hoffset = (reg_v & 0xffff); - - src_hactive *= tdms_clk_div; - src_hoffset *= tdms_clk_div; - - src_htotal = (reg_v & 0xffff0000)>>16; - src_htotal *= tdms_clk_div; - - rk628_i2c_read(rk628, 0x30148, ®_v); - reg_v = reg_v & 0xffff; - src_hs_end = reg_v * tdms_clk * tdms_clk_div / imodet_clk; - - //get vertical timing - rk628_i2c_read(rk628, 0x30168, ®_v); - src_vactive = reg_v & 0xffff; - rk628_i2c_read(rk628, 0x30170, ®_v); - src_vtotal = reg_v & 0xffff; - rk628_i2c_read(rk628, 0x30164, ®_v); - src_voffset = (reg_v & 0xffff); - - rk628_i2c_read(rk628, 0x3015c, ®_v); - reg_v = reg_v & 0xffff; - src_vs_end = reg_v * clk_rx_read; - do_div(src_vs_end, imodet_clk * src_htotal); - - //get fps and print - fps = clk_rx_read * 1000; - do_div(fps, src_htotal * src_vtotal); - DEBUG_PRINT(" Display mode: %dx%dp%d,dclk[%llu],tdms_clk[%d]\n", - src_hactive, src_vactive, fps, clk_rx_read, tdms_clk); - - DEBUG_PRINT("\tH: %d %d %d %d\n", src_hactive, src_htotal - src_hoffset, - src_htotal - src_hoffset + src_hs_end, src_htotal); - - DEBUG_PRINT("\tV: %d %d %d %d\n", src_vactive, - src_vtotal - src_voffset - src_vs_end, - src_vtotal - src_voffset, src_vtotal); - } else if (input_mode == 2 || input_mode == 3 || input_mode == 4) { - //get timing - rk628_i2c_read(rk628, 0x130, ®_v); - src_hactive = reg_v & 0xffff; - - rk628_i2c_read(rk628, 0x12c, ®_v); - src_vactive = (reg_v & 0xffff); - - rk628_i2c_read(rk628, 0x134, ®_v); - src_htotal = (reg_v & 0xffff0000) >> 16; - src_vtotal = reg_v & 0xffff; - - //get fps and print - fps = clk_rx_read * 1000; - do_div(fps, src_htotal * src_vtotal); - DEBUG_PRINT(" Display mode: %dx%dp%d,dclk[%llu]\n", - src_hactive, src_vactive, fps, clk_rx_read); - - DEBUG_PRINT("\tH-total: %d\n", src_htotal); - - DEBUG_PRINT("\tV-total: %d\n", src_vtotal); - } - //get output interface type - switch (output_mode & 0x7) { - case 1: - strcpy(output_s, "GVI"); - break; - case 2: - strcpy(output_s, "LVDS"); - break; - case 3: - strcpy(output_s, "HDMI"); - break; - case 4: - strcpy(output_s, "CSI"); - break; - case 5: - strcpy(output_s, "DSI"); - break; - default: - strcpy(output_s, ""); - } - strcpy(output_s + 4, " "); - switch (output_mode >> 2) { - case 0: - strcpy(output_s + 5, ""); - break; - case 1: - strcpy(output_s + 5, "BT1120"); - break; - case 2: - strcpy(output_s + 5, "RGB"); - break; - case 3: - strcpy(output_s + 5, "YUV"); - break; - default: - strcpy(output_s + 5, "unknown"); - } - DEBUG_PRINT("output:%s\n", output_s); - - //get output timing + /* get timing */ rk628_i2c_read(rk628, GRF_SCALER_CON3, &val); dsp_htotal = val & 0xffff; dsp_hs_end = (val & 0xff0000) >> 16; @@ -965,20 +1057,41 @@ static int rk628_debugfs_dump(struct seq_file *s, void *data) dsp_vact_st = (val & 0xfff0000) >> 16; dsp_vact_end = val & 0xfff; + /* get fps */ fps = sclk_vop * 1000; do_div(fps, dsp_vtotal * dsp_htotal); + /* print */ DEBUG_PRINT(" Display mode: %dx%dp%d,dclk[%llu]\n", - dsp_hact_end - dsp_hact_st, dsp_vact_end - dsp_vact_st, fps, sclk_vop); + dsp_hact_end - dsp_hact_st, dsp_vact_end - dsp_vact_st, fps, + sclk_vop); DEBUG_PRINT("\tH: %d %d %d %d\n", dsp_hact_end - dsp_hact_st, - dsp_htotal - dsp_hact_st, dsp_htotal - dsp_hact_st + dsp_hs_end, dsp_htotal); + dsp_htotal - dsp_hact_st, + dsp_htotal - dsp_hact_st + dsp_hs_end, dsp_htotal); DEBUG_PRINT("\tV: %d %d %d %d\n", dsp_vact_end - dsp_vact_st, - dsp_vtotal - dsp_vact_st, dsp_vtotal - dsp_vact_st + dsp_vs_end, dsp_vtotal); + dsp_vtotal - dsp_vact_st, + dsp_vtotal - dsp_vact_st + dsp_vs_end, dsp_vtotal); +} + +static void rk628_show_csc_info(struct seq_file *s) +{ + struct rk628 *rk628 = s->private; + u32 val; + bool r2y, y2r; + char csc_mode_r2y_s[10]; + char csc_mode_y2r_s[10]; + u32 csc; + enum csc_mode { + BT601_L, + BT709_L, + BT601_F, + BT2020 + }; - //get csc and system information rk628_i2c_read(rk628, GRF_CSC_CTRL_CON, &val); r2y = ((val & 0x10) == 0x10); y2r = ((val & 0x1) == 0x1); + csc = (val & 0xc0) >> 6; switch (csc) { case BT601_L: @@ -1019,13 +1132,38 @@ static int rk628_debugfs_dump(struct seq_file *s, void *data) DEBUG_PRINT("\ty2r[1],csc mode:%s\n", csc_mode_y2r_s); else DEBUG_PRINT("\tnot open\n"); +} + +static int rk628_debugfs_dump(struct seq_file *s, void *data) +{ + struct rk628 *rk628 = s->private; + u32 val; + int sw_hsync_pol, sw_vsync_pol; + u32 dsp_frame_v_start, dsp_frame_h_start; + + /* show input info */ + rk628_show_input_mode(s); + rk628_show_input_resolution(s); + + /* show output info */ + rk628_show_output_mode(s); + rk628_show_output_resolution(s); + + /* show csc info */ + rk628_show_csc_info(s); + + /* show other info */ + rk628_i2c_read(rk628, GRF_SYSTEM_CON0, &val); + sw_hsync_pol = (val & 0x4000000) ? 1 : 0; + sw_vsync_pol = (val & 0x2000000) ? 1 : 0; rk628_i2c_read(rk628, GRF_SCALER_CON2, &val); dsp_frame_h_start = val & 0xffff; dsp_frame_v_start = (val & 0xffff0000) >> 16; DEBUG_PRINT("system:\n"); - DEBUG_PRINT("\tsw_hsync_pol:%d, sw_vsync_pol:%d\n", sw_hsync_pol, sw_vsync_pol); + DEBUG_PRINT("\tsw_hsync_pol:%d, sw_vsync_pol:%d\n", sw_hsync_pol, + sw_vsync_pol); DEBUG_PRINT("\tdsp_frame_h_start:%d, dsp_frame_v_start:%d\n", dsp_frame_h_start, dsp_frame_v_start); @@ -1049,17 +1187,182 @@ static const struct file_operations rk628_debugfs_summary_fops = { }; +static int rk628_version_info(struct rk628 *rk628) +{ + int ret; + char *version; + + ret = rk628_i2c_read(rk628, GRF_SOC_VERSION, &rk628->version); + if (ret) { + dev_err(rk628->dev, "failed to access register: %d\n", ret); + return ret; + } + + switch (rk628->version) { + case RK628D_VERSION: + version = "D/E"; + break; + case RK628F_VERSION: + version = "F/H"; + break; + default: + version = "unknown"; + ret = -EINVAL; + } + + dev_info(rk628->dev, "the driver version is %s of RK628-%s\n", DRIVER_VERSION, version); + + return ret; +} + +static int rk628_reg_show(struct seq_file *s, void *v) +{ + const struct regmap_config *reg; + struct rk628 *rk628 = s->private; + unsigned int i, j; + u32 val; + + seq_printf(s, "rk628_%s:\n", file_dentry(s->file)->d_iname); + + for (i = 0; i < ARRAY_SIZE(rk628_regmap_config); i++) { + reg = &rk628_regmap_config[i]; + if (!reg->name) + continue; + if (!strcmp(reg->name, file_dentry(s->file)->d_iname)) + break; + } + + if (i == ARRAY_SIZE(rk628_regmap_config)) + return -ENODEV; + + /* grf */ + if (!reg->rd_table) { + for (i = 0; i <= reg->max_register; i += 4) { + rk628_i2c_read(rk628, i, &val); + if (i % 16 == 0) + seq_printf(s, "\n0x%04x:", i); + seq_printf(s, " %08x", val); + } + } else { + const struct regmap_range *range_list = reg->rd_table->yes_ranges; + const struct regmap_range *range; + int range_list_len = reg->rd_table->n_yes_ranges; + + for (i = 0; i < range_list_len; i++) { + range = &range_list[i]; + for (j = range->range_min; j <= range->range_max; j += 4) { + rk628_i2c_read(rk628, j, &val); + if (j % 16 == 0 || j == range->range_min) + seq_printf(s, "\n0x%04x:", j); + seq_printf(s, " %08x", val); + } + } + + seq_puts(s, "\n"); + } + + return 0; +} + +static ssize_t rk628_reg_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + const struct regmap_config *reg; + struct rk628 *rk628 = file->f_path.dentry->d_inode->i_private; + int i; + u32 addr; + u32 val; + char kbuf[25]; + int ret; + + if (count >= sizeof(kbuf)) + return -ENOSPC; + + if (copy_from_user(kbuf, buf, count)) + return -EFAULT; + + kbuf[count] = '\0'; + + ret = sscanf(kbuf, "%x%x", &addr, &val); + if (ret != 2) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(rk628_regmap_config); i++) { + reg = &rk628_regmap_config[i]; + if (!reg->name) + continue; + if (!strcmp(reg->name, file_dentry(file)->d_iname)) + break; + } + + if (i == ARRAY_SIZE(rk628_regmap_config)) + return -ENODEV; + + rk628_i2c_write(rk628, addr, val); + + return count; +} + +static int rk628_reg_open(struct inode *inode, struct file *file) +{ + struct rk628 *rk628 = inode->i_private; + + return single_open(file, rk628_reg_show, rk628); +} + +static const struct file_operations rk628_reg_fops = { + .owner = THIS_MODULE, + .open = rk628_reg_open, + .read = seq_read, + .write = rk628_reg_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static void rk628_debugfs_register_create(struct rk628 *rk628) +{ + const struct regmap_config *reg; + struct dentry *dir; + int i; + + dir = debugfs_create_dir("registers", rk628->debug_dir); + if (IS_ERR(dir)) + return; + + for (i = 0; i < ARRAY_SIZE(rk628_regmap_config); i++) { + reg = &rk628_regmap_config[i]; + if (!reg->name) + continue; + debugfs_create_file(reg->name, 0600, dir, rk628, &rk628_reg_fops); + } +} + +static void rk628_debugfs_create(struct rk628 *rk628) +{ + rk628->debug_dir = debugfs_create_dir(dev_name(rk628->dev), debugfs_lookup("rk628", NULL)); + if (IS_ERR(rk628->debug_dir)) + return; + + /* path example: /sys/kernel/debug/rk628/2-0050/summary */ + debugfs_create_file("summary", 0400, rk628->debug_dir, rk628, + &rk628_debugfs_summary_fops); + + rk628_debugfs_register_create(rk628); + + rk628_rgb_decoder_create_debugfs_file(rk628); + rk628_post_process_create_debugfs_file(rk628); + rk628_mipi_dsi_create_debugfs_file(rk628); + rk628_gvi_create_debugfs_file(rk628); + rk628_hdmitx_create_debugfs_file(rk628); +} + static int rk628_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct rk628 *rk628; int i, ret; - int err; unsigned long irq_flags; - struct dentry *debug_dir; - - dev_info(dev, "RK628 misc driver version: %s\n", DRIVER_VERSION); rk628 = devm_kzalloc(dev, sizeof(*rk628), GFP_KERNEL); if (!rk628) @@ -1072,14 +1375,13 @@ rk628_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) ret = rk628_display_route_info_parse(rk628); if (ret) { - dev_err(dev, "display route err\n"); + dev_err(dev, "display route parse err\n"); return ret; } - if (rk628->output_mode != OUTPUT_MODE_HDMI && - rk628->output_mode != OUTPUT_MODE_CSI) { + if (!rk628_output_is_csi(rk628)) { ret = rk628_display_timings_get(rk628); - if (ret) { + if (ret && !rk628_output_is_hdmi(rk628)) { dev_info(dev, "display timings err\n"); return ret; } @@ -1102,14 +1404,14 @@ rk628_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) if (IS_ERR(rk628->enable_gpio)) { ret = PTR_ERR(rk628->enable_gpio); dev_err(dev, "failed to request enable GPIO: %d\n", ret); - return ret; + goto err_clk; } rk628->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(rk628->reset_gpio)) { ret = PTR_ERR(rk628->reset_gpio); dev_err(dev, "failed to request reset GPIO: %d\n", ret); - return ret; + goto err_clk; } rk628->plugin_det_gpio = devm_gpiod_get_optional(dev, "plugin-det", @@ -1117,17 +1419,10 @@ rk628_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) if (IS_ERR(rk628->plugin_det_gpio)) { dev_err(rk628->dev, "failed to get hdmirx det gpio\n"); ret = PTR_ERR(rk628->plugin_det_gpio); - return ret; + goto err_clk; } - gpiod_set_value(rk628->enable_gpio, 1); - usleep_range(10000, 11000); - gpiod_set_value(rk628->reset_gpio, 0); - usleep_range(10000, 11000); - gpiod_set_value(rk628->reset_gpio, 1); - usleep_range(10000, 11000); - gpiod_set_value(rk628->reset_gpio, 0); - usleep_range(10000, 11000); + rk628_power_on(rk628, true); for (i = 0; i < RK628_DEV_MAX; i++) { const struct regmap_config *config = &rk628_regmap_config[i]; @@ -1140,22 +1435,33 @@ rk628_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) ret = PTR_ERR(rk628->regmap[i]); dev_err(dev, "failed to allocate register map %d: %d\n", i, ret); - return ret; + goto err_clk; } } - /* selete int io function */ - ret = rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x30002000); - if (ret) { - dev_err(dev, "failed to access register: %d\n", ret); - return ret; + ret = rk628_version_info(rk628); + if (ret) + goto err_clk; + + if (!rk628_display_route_check(rk628)) { + dev_err(dev, "display route check err\n"); + ret = -EINVAL; + goto err_clk; } + rk628_final_display_route(rk628); + rk628_pwr_consumption_init(rk628); + +#ifdef CONFIG_FB + rk628->fb_nb.notifier_call = rk628_fb_notifier_callback; + fb_register_client(&rk628->fb_nb); +#endif + rk628->monitor_wq = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM | WQ_FREEZABLE, "rk628-monitor-wq"); INIT_DELAYED_WORK(&rk628->delay_work, rk628_display_work); - if (rk628->output_mode == OUTPUT_MODE_DSI) { + if (rk628_output_is_dsi(rk628)) { rk628->dsi_wq = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM | WQ_FREEZABLE, "rk628-dsi-wq"); INIT_DELAYED_WORK(&rk628->dsi_delay_work, rk628_dsi_work); @@ -1163,47 +1469,48 @@ rk628_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) rk628_cru_init(rk628); - if (rk628->output_mode == OUTPUT_MODE_CSI) + if (rk628_output_is_csi(rk628)) rk628_csi_init(rk628); - if (rk628->input_mode == INPUT_MODE_HDMI) { + if (rk628_input_is_hdmi(rk628)) { if (rk628->plugin_det_gpio) { rk628->plugin_irq = gpiod_to_irq(rk628->plugin_det_gpio); if (rk628->plugin_irq < 0) { dev_err(rk628->dev, "failed to get plugin det irq\n"); - err = rk628->plugin_irq; - return err; + ret = rk628->plugin_irq; + goto err_clk; } - err = devm_request_threaded_irq(dev, rk628->plugin_irq, NULL, - rk628_hdmirx_plugin_irq, IRQF_TRIGGER_FALLING | - IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rk628_hdmirx", rk628); - if (err) { + ret = devm_request_threaded_irq(dev, rk628->plugin_irq, NULL, + rk628_hdmirx_plugin_irq, + IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "rk628_hdmirx", rk628); + if (ret) { dev_err(rk628->dev, "failed to register plugin det irq (%d)\n", - err); - return err; + ret); + goto err_clk; } if (rk628->hdmirx_irq) { irq_flags = irqd_get_trigger_type(irq_get_irq_data(rk628->hdmirx_irq)); dev_dbg(rk628->dev, "cfg hdmirx irq, flags: %lu!\n", irq_flags); - err = devm_request_threaded_irq(dev, rk628->hdmirx_irq, NULL, - rk628_hdmirx_plugin_irq, irq_flags | - IRQF_ONESHOT, "rk628", rk628); - if (err) { + ret = devm_request_threaded_irq(dev, rk628->hdmirx_irq, NULL, + rk628_hdmirx_plugin_irq, + irq_flags | IRQF_ONESHOT, + "rk628", rk628); + if (ret) { dev_err(rk628->dev, "request rk628 irq failed! err:%d\n", - err); - return err; + ret); + goto err_clk; } /* hdmirx int en */ - rk628_i2c_write(rk628, GRF_INTR0_EN, 0x01000100); - rk628_display_enable(rk628); + rk628_set_hdmirx_irq(rk628, GRF_INTR0_EN, true); queue_delayed_work(rk628->monitor_wq, &rk628->delay_work, msecs_to_jiffies(20)); } } else { - rk628_display_enable(rk628); queue_delayed_work(rk628->monitor_wq, &rk628->delay_work, msecs_to_jiffies(50)); } @@ -1212,38 +1519,53 @@ rk628_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) } pm_runtime_enable(dev); - debug_dir = debugfs_create_dir(rk628->dev->driver->name, NULL); - if (!debug_dir) - return 0; - - debugfs_create_file("summary", 0400, debug_dir, rk628, &rk628_debugfs_summary_fops); + rk628_debugfs_create(rk628); return 0; + +err_clk: + clk_disable_unprepare(rk628->soc_24M); + return ret; } +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE +static void rk628_i2c_remove(struct i2c_client *client) +#else static int rk628_i2c_remove(struct i2c_client *client) +#endif { struct rk628 *rk628 = i2c_get_clientdata(client); struct device *dev = &client->dev; - if (rk628->output_mode == OUTPUT_MODE_DSI) { + debugfs_remove_recursive(rk628->debug_dir); + + if (rk628_output_is_dsi(rk628)) { cancel_delayed_work_sync(&rk628->dsi_delay_work); destroy_workqueue(rk628->dsi_wq); } +#ifdef CONFIG_FB + fb_unregister_client(&rk628->fb_nb); +#endif + rk628_set_hdmirx_irq(rk628, GRF_INTR0_EN, false); cancel_delayed_work_sync(&rk628->delay_work); destroy_workqueue(rk628->monitor_wq); pm_runtime_disable(dev); - + clk_disable_unprepare(rk628->soc_24M); +#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE return 0; +#endif } +#ifndef CONFIG_FB #ifdef CONFIG_PM_SLEEP static int rk628_suspend(struct device *dev) { struct rk628 *rk628 = dev_get_drvdata(dev); + rk628_set_hdmirx_irq(rk628, GRF_INTR0_EN, false); rk628_display_disable(rk628); + rk628_power_on(rk628, false); return 0; } @@ -1252,18 +1574,39 @@ static int rk628_resume(struct device *dev) { struct rk628 *rk628 = dev_get_drvdata(dev); - rk628_display_resume(rk628); + rk628_power_on(rk628, true); + rk628_pwr_consumption_init(rk628); + rk628_cru_init(rk628); + + if (rk628_input_is_hdmi(rk628)) { + rk628_set_hdmirx_irq(rk628, GRF_INTR0_EN, true); + /* + * make hdmi rx register domain polling + * access to be normal by setting hdmi in + */ + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, + SW_INPUT_MODE_MASK, + SW_INPUT_MODE(INPUT_MODE_HDMI)); + + return 0; + } + + rk628_display_enable(rk628); return 0; } #endif +#endif static const struct dev_pm_ops rk628_pm_ops = { +#ifndef CONFIG_FB #ifdef CONFIG_PM_SLEEP .suspend = rk628_suspend, .resume = rk628_resume, #endif +#endif }; + static const struct of_device_id rk628_of_match[] = { { .compatible = "rockchip,rk628", }, {} @@ -1287,23 +1630,26 @@ static struct i2c_driver rk628_i2c_driver = { .id_table = rk628_i2c_id, }; -#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT_RK628 + static int __init rk628_i2c_driver_init(void) { + debugfs_create_dir("rk628", NULL); i2c_add_driver(&rk628_i2c_driver); return 0; } +#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT_RK628 subsys_initcall_sync(rk628_i2c_driver_init); +#else +module_init(rk628_i2c_driver_init); +#endif static void __exit rk628_i2c_driver_exit(void) { + debugfs_remove_recursive(debugfs_lookup("rk628", NULL)); i2c_del_driver(&rk628_i2c_driver); } module_exit(rk628_i2c_driver_exit); -#else -module_i2c_driver(rk628_i2c_driver); -#endif MODULE_AUTHOR("Wyon Bi "); MODULE_DESCRIPTION("Rockchip RK628 MFD driver"); diff --git a/drivers/misc/rk628/rk628.h b/drivers/misc/rk628/rk628.h index 82e39d4725e7..4d58c350a53a 100644 --- a/drivers/misc/rk628/rk628.h +++ b/drivers/misc/rk628/rk628.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,7 +19,7 @@ #include #include -#define DRIVER_VERSION "0.0.1" +#define DRIVER_VERSION "0.1.0" #define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l))) #define HIWORD_UPDATE(v, h, l) ((((v) << (l)) & GENMASK((h), (l))) | \ (GENMASK((h), (l)) << 16)) @@ -38,8 +39,16 @@ #define SW_BT_DATA_OEN BIT(9) #define SW_EFUSE_HDCP_EN_MASK BIT(8) #define SW_EFUSE_HDCP_EN(x) UPDATE(x, 8, 8) -#define SW_OUTPUT_MODE_MASK GENMASK(7, 3) -#define SW_OUTPUT_MODE(x) UPDATE(x, 7, 3) +#define SW_OUTPUT_MODE_MASK GENMASK(5, 3) +#define SW_OUTPUT_MODE(x) UPDATE(x, 5, 3) +/* compatible with rk628f */ +#define SW_OUTPUT_RGB_MODE_MASK GENMASK(7, 6) +#define SW_OUTPUT_RGB_MODE(x) UPDATE(x, 7, 6) +#define SW_HDMITX_EN_MASK BIT(5) +#define SW_HDMITX_EN(x) UPDATE(x, 5, 5) +#define SW_OUTPUT_COMBTX_MODE_MASK GENMASK(4, 3) +#define SW_OUTPUT_COMBTX_MODE(x) UPDATE(x, 4, 3) + #define SW_INPUT_MODE_MASK GENMASK(2, 0) #define SW_INPUT_MODE(x) UPDATE(x, 2, 0) #define GRF_SYSTEM_CON1 0x0004 @@ -51,7 +60,14 @@ #define GRF_GPIO_RXDDC_SDA_SEL(x) UPDATE(x, 6, 6) #define GRF_GPIO_RXDDC_SCL_SEL_MASK BIT(5) #define GRF_GPIO_RXDDC_SCL_SEL(x) UPDATE(x, 5, 5) +#define GRF_DPHY_CH1_EN_MASK BIT(1) +#define GRF_DPHY_CH1_EN(x) UPDATE(x, 1, 1) +#define GRF_AS_DSIPHY_MASK BIT(0) +#define GRF_AS_DSIPHY(x) UPDATE(x, 0, 0) #define GRF_SCALER_CON0 0x0010 +#define SCL_8_PIXEL_ALIGN(x) HIWORD_UPDATE(x, 12, 12) +#define SCL_COLOR_VER_EN(x) HIWORD_UPDATE(x, 10, 10) +#define SCL_COLOR_BAR_EN(x) HIWORD_UPDATE(x, 9, 9) #define SCL_VER_DOWN_MODE(x) HIWORD_UPDATE(x, 8, 8) #define SCL_HOR_DOWN_MODE(x) HIWORD_UPDATE(x, 7, 7) #define SCL_BIC_COE_SEL(x) HIWORD_UPDATE(x, 6, 5) @@ -83,6 +99,8 @@ #define DSP_VBOR_ST(x) UPDATE(x, 28, 16) #define DSP_VBOR_END(x) UPDATE(x, 12, 0) #define GRF_POST_PROC_CON 0x0034 +#define SW_HDMITX_VSYNC_POL BIT(17) +#define SW_HDMITX_HSYNC_POL BIT(16) #define SW_DCLK_OUT_INV_EN BIT(9) #define SW_DCLK_IN_INV_EN BIT(8) #define SW_TXPHY_REFCLK_SEL_MASK GENMASK(6, 5) @@ -93,6 +111,8 @@ #define SW_SPLIT_MODE(x) UPDATE(x, 1, 1) #define SW_SPLIT_EN BIT(0) #define GRF_CSC_CTRL_CON 0x0038 +#define SW_Y2R_MODE(x) HIWORD_UPDATE(x, 13, 12) +#define SW_FROM_CSC_MATRIX_EN(x) HIWORD_UPDATE(x, 11, 11) #define SW_YUV2VYU_SWP(x) HIWORD_UPDATE(x, 8, 8) #define SW_R2Y_EN(x) HIWORD_UPDATE(x, 4, 4) #define SW_Y2R_EN(x) HIWORD_UPDATE(x, 0, 0) @@ -179,7 +199,10 @@ #define GRF_GPIO3A_D1_CON 0x00e4 #define GRF_GPIO3B_D_CON 0x00e8 #define GRF_GPIO_SR_CON 0x00ec +#define GRF_SW_HDMIRXPHY_CRTL 0x00f4 #define GRF_INTR0_EN 0x0100 +#define RK628F_HDMIRX_IRQ_EN(x) HIWORD_UPDATE(x, 9, 9) +#define RK628D_HDMIRX_IRQ_EN(x) HIWORD_UPDATE(x, 8, 8) #define GRF_INTR0_CLR_EN 0x0104 #define GRF_INTR0_STATUS 0x0108 #define GRF_INTR0_RAW_STATUS 0x010c @@ -191,18 +214,53 @@ /* 0: i2c mode and mcu mode; 1: i2c mode only */ #define I2C_ONLY_FLAG BIT(6) #define GRF_SYSTEM_STATUS3 0x012c +#define DECODER_1120_LAST_LINE_NUM_MASK GENMASK(12, 0) #define GRF_SYSTEM_STATUS4 0x0130 +#define DECODER_1120_LAST_PIX_NUM_MASK GENMASK(12, 0) #define GRF_OS_REG0 0x0140 #define GRF_OS_REG1 0x0144 #define GRF_OS_REG2 0x0148 #define GRF_OS_REG3 0x014c -#define GRF_SOC_VERSION 0x0150 -#define GRF_MAX_REGISTER GRF_SOC_VERSION +#define GRF_RGB_RX_DBG_MEAS0 0x0170 +#define RGB_RX_EVAL_TIME_MASK GENMASK(27, 16) +#define RGB_RX_MODET_EN BIT(1) +#define RGB_RX_DCLK_EN BIT(0) +#define GRF_RGB_RX_DBG_MEAS2 0x0178 +#define RGB_RX_CLKRATE_MASK GENMASK(15, 0) +#define GRF_RGB_RX_DBG_MEAS3 0x017c +#define RGB_RX_CNT_EN_MASK BIT(0) +#define RGB_RX_CNT_EN(x) UPDATE(x, 0, 0) +#define GRF_RGB_RX_DBG_MEAS4 0x0180 +#define GRF_BT1120_TIMING_CTRL0 0x0190 +#define BT1120_DSP_HS_END(x) UPDATE(x, 28, 16) +#define BT1120_DSP_HTOTAL(x) UPDATE(x, 12, 0) +#define GRF_BT1120_TIMING_CTRL1 0x0194 +#define BT1120_DSP_HACT_ST(x) UPDATE(x, 28, 16) +#define GRF_BT1120_TIMING_CTRL2 0x0198 +#define BT1120_DSP_VS_END(x) UPDATE(x, 28, 16) +#define BT1120_DSP_VTOTAL(x) UPDATE(x, 12, 0) +#define GRF_BT1120_TIMING_CTRL3 0x019c +#define BT1120_DSP_VACT_ST(x) UPDATE(x, 28, 16) +#define GRF_CSC_MATRIX_COE01_COE00 0x01a0 +#define GRF_CSC_MATRIX_COE10_COE02 0x01a4 +#define GRF_CSC_MATRIX_COE12_COE11 0x01a8 +#define GRF_CSC_MATRIX_COE21_COE20 0x01ac +#define GRF_CSC_MATRIX_COE22 0x01b0 +#define GRF_CSC_MATRIX_OFFSET0 0x01b4 +#define GRF_CSC_MATRIX_OFFSET1 0x01b8 +#define GRF_CSC_MATRIX_OFFSET2 0x01bc +#define GRF_SOC_VERSION 0x0200 +#define GRF_OBS_REG 0X0300 +#define GRF_MAX_REGISTER GRF_OBS_REG #define DRM_MODE_FLAG_PHSYNC (1<<0) #define DRM_MODE_FLAG_NHSYNC (1<<1) #define DRM_MODE_FLAG_PVSYNC (1<<2) #define DRM_MODE_FLAG_NVSYNC (1<<3) +#define DRM_MODE_FLAG_INTERLACE (1<<4) + +#define RK628D_VERSION 0x20200326 +#define RK628F_VERSION 0x20230321 enum { COMBTXPHY_MODULEA_EN = BIT(0), @@ -317,7 +375,63 @@ enum rk628_mode_sync_pol { MODE_FLAG_PSYNC, }; -#undef BT1120_DUAL_EDGE +/* see also http://vektor.theorem.ca/graphics/ycbcr/ */ +enum rk628_v4l2_colorspace { + /* + * Default colorspace, i.e. let the driver figure it out. + * Can only be used with video capture. + */ + V4L2_COLORSPACE_DEFAULT = 0, + + /* SMPTE 170M: used for broadcast NTSC/PAL SDTV */ + V4L2_COLORSPACE_SMPTE170M = 1, + + /* Obsolete pre-1998 SMPTE 240M HDTV standard, superseded by Rec 709 */ + V4L2_COLORSPACE_SMPTE240M = 2, + + /* Rec.709: used for HDTV */ + V4L2_COLORSPACE_REC709 = 3, + + /* + * Deprecated, do not use. No driver will ever return this. This was + * based on a misunderstanding of the bt878 datasheet. + */ + V4L2_COLORSPACE_BT878 = 4, + + /* + * NTSC 1953 colorspace. This only makes sense when dealing with + * really, really old NTSC recordings. Superseded by SMPTE 170M. + */ + V4L2_COLORSPACE_470_SYSTEM_M = 5, + + /* + * EBU Tech 3213 PAL/SECAM colorspace. This only makes sense when + * dealing with really old PAL/SECAM recordings. Superseded by + * SMPTE 170M. + */ + V4L2_COLORSPACE_470_SYSTEM_BG = 6, + + /* + * Effectively shorthand for V4L2_COLORSPACE_SRGB, V4L2_YCBCR_ENC_601 + * and V4L2_QUANTIZATION_FULL_RANGE. To be used for (Motion-)JPEG. + */ + V4L2_COLORSPACE_JPEG = 7, + + /* For RGB colorspaces such as produces by most webcams. */ + V4L2_COLORSPACE_SRGB = 8, + + /* opRGB colorspace */ + V4L2_COLORSPACE_OPRGB = 9, + + /* BT.2020 colorspace, used for UHDTV. */ + V4L2_COLORSPACE_BT2020 = 10, + + /* Raw colorspace: for RAW unprocessed images */ + V4L2_COLORSPACE_RAW = 11, + + /* DCI-P3 colorspace, used by cinema projectors */ + V4L2_COLORSPACE_DCI_P3 = 12, +}; struct rk628_videomode { u32 pixelclock; /* pixelclock in Hz */ @@ -366,7 +480,7 @@ struct panel_cmds { int cmd_cnt; }; -struct panel_simple { +struct rk628_panel_simple { struct backlight_device *backlight; struct regulator *supply; @@ -374,6 +488,15 @@ struct panel_simple { struct gpio_desc *reset_gpio; struct panel_cmds *on_cmds; struct panel_cmds *off_cmds; + + struct { + unsigned int prepare; + unsigned int enable; + unsigned int disable; + unsigned int unprepare; + unsigned int reset; + unsigned int init; + } delay; }; struct rk628_dsi { @@ -386,6 +509,7 @@ struct rk628_dsi { uint8_t lanes; uint8_t id; /* 0:dsi0 1:dsi1 */ struct rk628 *rk628; + unsigned int lane_mbps; /* per lane */ }; struct rk628_lvds { @@ -396,6 +520,7 @@ struct rk628_lvds { struct rk628_gvi { enum gvi_bus_format bus_format; enum gvi_color_depth color_depth; + int retry_times; uint8_t lanes; bool division_mode; bool frm_rst; @@ -413,6 +538,11 @@ struct rk628_combtxphy { bool division_mode; }; +struct rk628_rgb { + struct regulator *vccio_rgb; + bool bt1120_dual_edge; +}; + struct rk628 { struct device *dev; struct i2c_client *client; @@ -425,13 +555,15 @@ struct rk628 { struct clk *soc_24M; struct workqueue_struct *monitor_wq; struct delayed_work delay_work; + struct dentry *debug_dir; struct workqueue_struct *dsi_wq; struct delayed_work dsi_delay_work; - struct panel_simple *panel; + struct rk628_panel_simple *panel; void *hdmirx; + void *hdmitx; bool display_enabled; - enum rk628_input_mode input_mode; - enum rk628_output_mode output_mode; + u32 input_mode; + u32 output_mode; struct rk628_display_mode src_mode; struct rk628_display_mode dst_mode; enum bus_format input_fmt; @@ -443,8 +575,62 @@ struct rk628 { struct rk628_combtxphy combtxphy; int sync_pol; void *csi; + struct notifier_block fb_nb; + u32 version; + struct rk628_rgb rgb; + int old_blank; }; +static inline bool rk628_input_is_hdmi(struct rk628 *rk628) +{ + return rk628->input_mode & BIT(INPUT_MODE_HDMI); +} + +static inline bool rk628_input_is_rgb(struct rk628 *rk628) +{ + return rk628->input_mode & BIT(INPUT_MODE_RGB); +} + +static inline bool rk628_input_is_bt1120(struct rk628 *rk628) +{ + return rk628->input_mode & BIT(INPUT_MODE_BT1120); +} + +static inline bool rk628_output_is_rgb(struct rk628 *rk628) +{ + return rk628->output_mode & BIT(OUTPUT_MODE_RGB); +} + +static inline bool rk628_output_is_bt1120(struct rk628 *rk628) +{ + return rk628->output_mode & BIT(OUTPUT_MODE_BT1120); +} + +static inline bool rk628_output_is_gvi(struct rk628 *rk628) +{ + return rk628->output_mode & BIT(OUTPUT_MODE_GVI); +} + +static inline bool rk628_output_is_lvds(struct rk628 *rk628) +{ + return rk628->output_mode & BIT(OUTPUT_MODE_LVDS); +} + +static inline bool rk628_output_is_dsi(struct rk628 *rk628) +{ + return rk628->output_mode & BIT(OUTPUT_MODE_DSI); +} + +static inline bool rk628_output_is_csi(struct rk628 *rk628) +{ + return rk628->output_mode & BIT(OUTPUT_MODE_CSI); +} + +static inline bool rk628_output_is_hdmi(struct rk628 *rk628) +{ + return rk628->output_mode & BIT(OUTPUT_MODE_HDMI); +} + static inline int rk628_i2c_write(struct rk628 *rk628, u32 reg, u32 val) { int region = (reg >> 16) & 0xff; diff --git a/drivers/misc/rk628/rk628_combtxphy.c b/drivers/misc/rk628/rk628_combtxphy.c index ddb4691b900d..3387d8bd73b9 100644 --- a/drivers/misc/rk628/rk628_combtxphy.c +++ b/drivers/misc/rk628/rk628_combtxphy.c @@ -41,10 +41,20 @@ static void rk628_combtxphy_dsi_power_on(struct rk628 *rk628) if (ret < 0) dev_err(rk628->dev, "phy is not lock\n"); - rk628_i2c_update_bits(rk628, COMBTXPHY_CON9, - SW_DSI_FSET_EN_MASK | SW_DSI_RCAL_EN_MASK, - SW_DSI_FSET_EN | SW_DSI_RCAL_EN); + SW_DSI_FSET_EN_MASK | SW_DSI_RCAL_EN_MASK, + SW_DSI_FSET_EN | SW_DSI_RCAL_EN(1)); + + if (rk628->version == RK628F_VERSION) { + rk628_i2c_update_bits(rk628, COMBTXPHY_CON6, + SW_PLL_CTL_CON0_MASK, + SW_PLL_CTL_CON0(1)); + rk628_i2c_update_bits(rk628, COMBTXPHY_CON9, + SW_DSI_LPTX_SR_TRIM_MASK | + SW_DSI_HSTX_AMP_TRIM_MASK, + SW_DSI_LPTX_SR_TRIM(0) | + SW_DSI_HSTX_AMP_TRIM(4)); + } usleep_range(200, 400); } @@ -57,11 +67,15 @@ static void rk628_combtxphy_lvds_power_on(struct rk628 *rk628) int ret; /* Adjust terminal resistance 133 ohm, bypass 0.95v ldo for driver. */ + if (rk628->version == RK628F_VERSION) + val = TX_COM_VOLT_ADJ(3); + else + val = TX_COM_VOLT_ADJ(0); rk628_i2c_update_bits(rk628, COMBTXPHY_CON7, SW_TX_RTERM_MASK | SW_TX_MODE_MASK | BYPASS_095V_LDO_MASK | TX_COM_VOLT_ADJ_MASK, SW_TX_RTERM(6) | SW_TX_MODE(3) | - BYPASS_095V_LDO(1) | TX_COM_VOLT_ADJ(0)); + BYPASS_095V_LDO(1) | val); rk628_i2c_write(rk628, COMBTXPHY_CON10, TX7_CKDRV_EN | TX2_CKDRV_EN); rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, @@ -263,7 +277,9 @@ void rk628_combtxphy_set_mode(struct rk628 *rk628, enum phy_mode mode) else combtxphy->rate_div = 1; fvco = bus_width * combtxphy->rate_div; - ref_clk = rk628_cru_clk_get_rate(rk628, CGU_SCLK_VOP) / 1000; /* khz */ + ref_clk = rk628_cru_clk_get_rate(rk628, CGU_SCLK_VOP); + ref_clk = DIV_ROUND_CLOSEST_ULL(ref_clk, 1000); /* khz */ + if (combtxphy->division_mode) ref_clk /= 2; /* @@ -284,7 +300,7 @@ void rk628_combtxphy_set_mode(struct rk628 *rk628, enum phy_mode mode) /* * ref_clk / ref_div * 8 * fb_div = FVCO */ - pre_clk = (unsigned long long)fvco / 8 * combtxphy->ref_div * 1024; + pre_clk = (unsigned long long)fvco * combtxphy->ref_div / 8 * 1024; do_div(pre_clk, ref_clk); fb_div = pre_clk / 1024; diff --git a/drivers/misc/rk628/rk628_combtxphy.h b/drivers/misc/rk628/rk628_combtxphy.h index b59594df8700..8276d1c212a1 100644 --- a/drivers/misc/rk628/rk628_combtxphy.h +++ b/drivers/misc/rk628/rk628_combtxphy.h @@ -41,6 +41,8 @@ #define SW_PLL_FB_DIV(x) UPDATE(x, 14, 10) #define SW_PLL_FRAC_DIV(x) UPDATE(x, 9, 0) #define COMBTXPHY_CON6 REG(0x0018) +#define SW_PLL_CTL_CON0_MASK GENMASK(2, 0) +#define SW_PLL_CTL_CON0(x) UPDATE(x, 2, 0) #define COMBTXPHY_CON7 REG(0x001c) #define SW_TX_RTERM_MASK GENMASK(22, 20) #define SW_TX_RTERM(x) UPDATE(x, 22, 20) @@ -60,7 +62,13 @@ #define SW_DSI_FSET_EN_MASK BIT(29) #define SW_DSI_FSET_EN BIT(29) #define SW_DSI_RCAL_EN_MASK BIT(28) -#define SW_DSI_RCAL_EN BIT(28) +#define SW_DSI_RCAL_EN(x) UPDATE(x, 28, 28) +#define SW_DSI_RCAL_TRIM_MASK GENMASK(27, 24) +#define SW_DSI_RCAL_TRIM(x) UPDATE(x, 27, 24) +#define SW_DSI_LPTX_SR_TRIM_MASK GENMASK(6, 4) +#define SW_DSI_LPTX_SR_TRIM(x) UPDATE(x, 6, 4) +#define SW_DSI_HSTX_AMP_TRIM_MASK GENMASK(2, 0) +#define SW_DSI_HSTX_AMP_TRIM(x) UPDATE(x, 2, 0) #define COMBTXPHY_CON10 REG(0x0028) #define TX9_CKDRV_EN BIT(9) #define TX8_CKDRV_EN BIT(8) diff --git a/drivers/misc/rk628/rk628_cru.c b/drivers/misc/rk628/rk628_cru.c index 31dc2d08e35a..87123010235b 100644 --- a/drivers/misc/rk628/rk628_cru.c +++ b/drivers/misc/rk628/rk628_cru.c @@ -5,6 +5,7 @@ * Author: Wyon Bi */ +#include "asm-generic/errno-base.h" #include "rk628.h" #include "rk628_cru.h" @@ -65,6 +66,9 @@ static unsigned long rk628_cru_clk_get_rate_pll(struct rk628 *rk628, u64 foutvco, foutpostdiv; u32 offset, val; + if (id == CGU_CLK_APLL && rk628->version < RK628F_VERSION) + return 0; + rk628_i2c_read(rk628, CRU_MODE_CON00, &val); if (id == CGU_CLK_CPLL) { val &= CLK_CPLL_MODE_MASK; @@ -73,13 +77,20 @@ static unsigned long rk628_cru_clk_get_rate_pll(struct rk628 *rk628, return parent_rate; offset = 0x00; - } else { + } else if (id == CGU_CLK_GPLL) { val &= CLK_GPLL_MODE_MASK; val >>= CLK_GPLL_MODE_SHIFT; if (val == CLK_GPLL_MODE_OSC) return parent_rate; offset = 0x20; + } else { + val &= CLK_APLL_MODE_MASK; + val >>= CLK_APLL_MODE_SHIFT; + if (val == CLK_APLL_MODE_OSC) + return parent_rate; + + offset = 0x40; } rk628_i2c_read(rk628, offset + CRU_CPLL_CON0, &con0); @@ -123,7 +134,7 @@ static unsigned long rk628_cru_clk_set_rate_pll(struct rk628 *rk628, u8 dsmpd = 1, postdiv1 = 0, postdiv2 = 0, refdiv = 0; u16 fbdiv = 0; u32 frac = 0; - u64 foutvco, foutpostdiv; + u64 foutvco, foutpostdiv, div1, div2; u32 offset, val; /* @@ -140,10 +151,13 @@ static unsigned long rk628_cru_clk_set_rate_pll(struct rk628 *rk628, if (id == CGU_CLK_CPLL) offset = 0x00; - else + else if (id == CGU_CLK_GPLL) offset = 0x20; + else + offset = 0x40; - rk628_i2c_write(rk628, offset + CRU_CPLL_CON1, PLL_PD(1)); + if (id != CGU_CLK_APLL) + rk628_i2c_write(rk628, offset + CRU_CPLL_CON1, PLL_PD(1)); if (fin == fout) { rk628_i2c_write(rk628, offset + CRU_CPLL_CON0, PLL_BYPASS(1)); @@ -162,21 +176,24 @@ static unsigned long rk628_cru_clk_set_rate_pll(struct rk628 *rk628, max_refdiv = 64; if (fout < MIN_FVCO_RATE) { - postdiv = MIN_FVCO_RATE / fout + 1; - - for (postdiv2 = 1; postdiv2 < 8; postdiv2++) { - if (postdiv % postdiv2) + div1 = DIV_ROUND_UP(MIN_FVCO_RATE, fout); + div2 = DIV_ROUND_UP(MAX_FVCO_RATE, fout); + for (postdiv = div1; postdiv <= div2; postdiv++) { + /* fix prime number that can not find right div*/ + for (postdiv2 = 1; postdiv2 < 8; postdiv2++) { + if (postdiv % postdiv2) + continue; + postdiv1 = postdiv / postdiv2; + if (postdiv1 > 0 && postdiv1 < 8) + break; + } + if (postdiv2 > 7) continue; - - postdiv1 = postdiv / postdiv2; - - if (postdiv1 > 0 && postdiv1 < 8) + else break; } - - if (postdiv2 > 7) + if (postdiv > div2) return 0; - fout *= postdiv1 * postdiv2; } else { postdiv1 = 1; @@ -244,7 +261,9 @@ static unsigned long rk628_cru_clk_set_rate_pll(struct rk628 *rk628, PLL_REFDIV(refdiv)); rk628_i2c_write(rk628, offset + CRU_CPLL_CON2, PLL_FRAC(frac)); - rk628_i2c_write(rk628, offset + CRU_CPLL_CON1, PLL_PD(0)); + if (id != CGU_CLK_APLL) + rk628_i2c_write(rk628, offset + CRU_CPLL_CON1, PLL_PD(0)); + while (1) { rk628_i2c_read(rk628, offset + CRU_CPLL_CON1, &val); if (val & PLL_LOCK) @@ -297,6 +316,26 @@ static unsigned long rk628_cru_clk_get_rate_sclk_vop(struct rk628 *rk628) return rate; } +static unsigned long rk628_cru_clk_get_rate_clk_imodet(struct rk628 *rk628) +{ + unsigned long rate, parent_rate, n; + u32 mux, div; + + rk628_i2c_read(rk628, CRU_CLKSEL_CON05, &mux); + mux &= CLK_IMODET_SEL_MASK; + mux >>= CLK_IMODET_SEL_SHIFT; + if (mux == SCLK_VOP_SEL_GPLL) + parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); + else + parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL); + + rk628_i2c_read(rk628, CRU_CLKSEL_CON05, &div); + n = div & 0x1f; + rate = parent_rate / (n + 1); + + return rate; +} + static unsigned long rk628_cru_clk_set_rate_rx_read(struct rk628 *rk628, unsigned long rate) { @@ -367,6 +406,51 @@ static unsigned long rk628_cru_clk_set_rate_sclk_uart(struct rk628 *rk628, return rate; } +static unsigned long rk628_cru_clk_set_rate_sclk_hdmirx_aud(struct rk628 *rk628, unsigned long rate) +{ + u64 parent_rate; + u8 div; + + if (rk628->version >= RK628F_VERSION) + parent_rate = rk628_cru_clk_set_rate_pll(rk628, CGU_CLK_APLL, rate*4); + else + parent_rate = rk628_cru_clk_set_rate_pll(rk628, CGU_CLK_GPLL, rate*4); + div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate); + do_div(parent_rate, div); + rate = parent_rate; + if (rk628->version >= RK628F_VERSION) + rk628_i2c_write(rk628, CRU_CLKSEL_CON05, CLK_HDMIRX_AUD_DIV(div - 1) | + CLK_HDMIRX_AUD_SEL_V2(2)); + else + rk628_i2c_write(rk628, CRU_CLKSEL_CON05, CLK_HDMIRX_AUD_DIV(div - 1) | + CLK_HDMIRX_AUD_SEL_V1(1)); + return rate; +} + +static unsigned long rk628_cru_clk_get_rate_sclk_hdmirx_aud(struct rk628 *rk628) +{ + unsigned long rate; + u64 parent_rate; + u8 div; + u32 val; + + rk628_i2c_read(rk628, CRU_CLKSEL_CON05, &val); + div = ((val & CLK_HDMIRX_AUD_DIV_MASK) >> 6) + 1; + if (rk628->version >= RK628F_VERSION) + val = (val & CLK_HDMIRX_AUD_SEL_MASK_V2) >> 14; + else + val = (val & CLK_HDMIRX_AUD_SEL_MASK_V1) >> 15; + if (!val) + parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL); + else if (val == 2) + parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_APLL); + else + parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL); + do_div(parent_rate, div); + rate = parent_rate; + return rate; +} + static unsigned long rk628_cru_clk_get_rate_bt1120_dec_parent(struct rk628 *rk628) { @@ -399,7 +483,11 @@ static unsigned long rk628_cru_clk_set_rate_bt1120_dec(struct rk628 *rk628, int rk628_cru_clk_set_rate(struct rk628 *rk628, unsigned int id, unsigned long rate) { + if (id == CGU_CLK_APLL && rk628->version < RK628F_VERSION) + return -EINVAL; + switch (id) { + case CGU_CLK_APLL: case CGU_CLK_CPLL: case CGU_CLK_GPLL: rk628_cru_clk_set_rate_pll(rk628, id, rate); @@ -416,8 +504,11 @@ int rk628_cru_clk_set_rate(struct rk628 *rk628, unsigned int id, case CGU_BT1120DEC: rk628_cru_clk_set_rate_bt1120_dec(rk628, rate); break; + case CGU_CLK_HDMIRX_AUD: + rk628_cru_clk_set_rate_sclk_hdmirx_aud(rk628, rate); + break; default: - return -1; + return -EINVAL; } return 0; @@ -427,7 +518,11 @@ unsigned long rk628_cru_clk_get_rate(struct rk628 *rk628, unsigned int id) { unsigned long rate; + if (id == CGU_CLK_APLL && rk628->version < RK628F_VERSION) + return 0; + switch (id) { + case CGU_CLK_APLL: case CGU_CLK_CPLL: case CGU_CLK_GPLL: rate = rk628_cru_clk_get_rate_pll(rk628, id); @@ -435,6 +530,12 @@ unsigned long rk628_cru_clk_get_rate(struct rk628 *rk628, unsigned int id) case CGU_SCLK_VOP: rate = rk628_cru_clk_get_rate_sclk_vop(rk628); break; + case CGU_CLK_IMODET: + rate = rk628_cru_clk_get_rate_clk_imodet(rk628); + break; + case CGU_CLK_HDMIRX_AUD: + rate = rk628_cru_clk_get_rate_sclk_hdmirx_aud(rk628); + break; default: return 0; } @@ -449,9 +550,10 @@ void rk628_cru_init(struct rk628 *rk628) rk628_i2c_read(rk628, GRF_SYSTEM_STATUS0, &val); mcu_mode = (val & I2C_ONLY_FLAG) ? 0 : 1; - if (mcu_mode) + if (mcu_mode || rk628->version >= RK628F_VERSION) { + rk628_i2c_write(rk628, CRU_MODE_CON00, HIWORD_UPDATE(1, 4, 4)); return; - + } rk628_i2c_write(rk628, CRU_GPLL_CON0, 0xffff701d); mdelay(1); rk628_i2c_write(rk628, CRU_MODE_CON00, 0xffff0004); diff --git a/drivers/misc/rk628/rk628_cru.h b/drivers/misc/rk628/rk628_cru.h index e13a559539de..e13b728eb915 100644 --- a/drivers/misc/rk628/rk628_cru.h +++ b/drivers/misc/rk628/rk628_cru.h @@ -46,7 +46,16 @@ #define CRU_GPLL_CON2 CRU_REG(0x0028) #define CRU_GPLL_CON3 CRU_REG(0x002c) #define CRU_GPLL_CON4 CRU_REG(0x0030) +#define CRU_APLL_CON0 CRU_REG(0x0040) +#define CRU_APLL_CON1 CRU_REG(0x0044) +#define CRU_APLL_CON2 CRU_REG(0x0048) +#define CRU_APLL_CON3 CRU_REG(0x004c) +#define CRU_APLL_CON4 CRU_REG(0x0050) #define CRU_MODE_CON00 CRU_REG(0x0060) +#define CLK_APLL_MODE_MASK BIT(4) +#define CLK_APLL_MODE_SHIFT 4 +#define CLK_APLL_MODE_GPLL 1 +#define CLK_APLL_MODE_OSC 0 #define CLK_GPLL_MODE_MASK BIT(2) #define CLK_GPLL_MODE_SHIFT 2 #define CLK_GPLL_MODE_GPLL 1 @@ -73,7 +82,19 @@ #define CLK_BT1120DEC_DIV(x) HIWORD_UPDATE(x, 4, 0) #define CRU_CLKSEL_CON03 CRU_REG(0x008c) #define CRU_CLKSEL_CON04 CRU_REG(0x0090) +#define CLK_HDMIRX_AUD_DIV_MASK GENMASK(13, 6) +#define CLK_HDMIRX_AUD_DIV(x) HIWORD_UPDATE(x, 13, 6) +#define CLK_HDMIRX_AUD_SEL_MASK GENMASK(15, 14) +#define CLK_HDMIRX_AUD_SEL(x) HIWORD_UPDATE(x, 15, 14) #define CRU_CLKSEL_CON05 CRU_REG(0x0094) +#define CLK_HDMIRX_AUD_DIV_MASK GENMASK(13, 6) +#define CLK_HDMIRX_AUD_DIV(x) HIWORD_UPDATE(x, 13, 6) +#define CLK_HDMIRX_AUD_SEL_V1(x) HIWORD_UPDATE(x, 15, 15) +#define CLK_HDMIRX_AUD_SEL_MASK_V1 GENMASK(15, 15) +#define CLK_HDMIRX_AUD_SEL_V2(x) HIWORD_UPDATE(x, 15, 14) +#define CLK_HDMIRX_AUD_SEL_MASK_V2 GENMASK(15, 14) +#define CLK_IMODET_SEL_MASK BIT(5) +#define CLK_IMODET_SEL_SHIFT 5 #define CRU_CLKSEL_CON06 CRU_REG(0x0098) #define SCLK_UART_SEL(x) HIWORD_UPDATE(x, 15, 14) #define SCLK_UART_SEL_MASK GENMASK(15, 14) @@ -150,6 +171,7 @@ #define CGU_I2S_MCLKOUT 36 #define CGU_BT1120DEC 37 #define CGU_SCLK_UART 38 +#define CGU_CLK_APLL 39 unsigned long rk628_cru_clk_get_rate(struct rk628 *rk628, unsigned int id); int rk628_cru_clk_set_rate(struct rk628 *rk628, unsigned int id, diff --git a/drivers/misc/rk628/rk628_csi.c b/drivers/misc/rk628/rk628_csi.c index a121c8f08173..dd3d4dd98c6c 100644 --- a/drivers/misc/rk628/rk628_csi.c +++ b/drivers/misc/rk628/rk628_csi.c @@ -416,8 +416,15 @@ static void enable_stream(struct rk628 *rk628, bool en) void rk628_csi_init(struct rk628 *rk628) { - rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, - SW_OUTPUT_MODE_MASK, SW_OUTPUT_MODE(OUTPUT_MODE_CSI)); + u32 mask = SW_OUTPUT_MODE_MASK; + u32 val = SW_OUTPUT_MODE(OUTPUT_MODE_CSI); + + if (rk628->version == RK628F_VERSION) { + mask = SW_OUTPUT_COMBTX_MODE_MASK; + val = SW_OUTPUT_COMBTX_MODE(OUTPUT_MODE_CSI - 1); + } + + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, mask, val); rk628_csi_get_detected_timings(rk628); mipi_dphy_reset(rk628); } diff --git a/drivers/misc/rk628/rk628_dsi.c b/drivers/misc/rk628/rk628_dsi.c index 368f704351e9..9f93bfe35ffe 100644 --- a/drivers/misc/rk628/rk628_dsi.c +++ b/drivers/misc/rk628/rk628_dsi.c @@ -6,6 +6,7 @@ */ #include +#include #include "rk628.h" #include "rk628_cru.h" #include "rk628_dsi.h" @@ -21,8 +22,6 @@ /* use Low Power Mode to transmit message */ #define MIPI_DSI_MSG_USE_LPM BIT(1) -static u32 lane_mbps; - enum vid_mode_type { VIDEO_MODE, COMMAND_MODE, @@ -237,7 +236,6 @@ int rk628_dsi_parse(struct rk628 *rk628, struct device_node *dsi_np) if (!of_device_is_available(dsi_np)) return -EINVAL; - rk628->output_mode = OUTPUT_MODE_DSI; rk628->dsi0.id = 0; rk628->dsi0.channel = 0; rk628->dsi0.rk628 = rk628; @@ -254,6 +252,9 @@ int rk628_dsi_parse(struct rk628 *rk628, struct device_node *dsi_np) if (of_property_read_bool(dsi_np, "dsi,eotp")) rk628->dsi0.mode_flags |= MIPI_DSI_MODE_EOT_PACKET; + if (of_property_read_bool(dsi_np, "dsi,clk-non-continuous")) + rk628->dsi0.mode_flags |= MIPI_DSI_CLOCK_NON_CONTINUOUS; + if (!of_property_read_string(dsi_np, "dsi,format", &string)) { if (!strcmp(string, "rgb666")) { rk628->dsi0.bus_format = MIPI_DSI_FMT_RGB666; @@ -697,8 +698,8 @@ static int rk628_dsi_transfer(struct rk628 *rk628, const struct rk628_dsi *dsi, return msg->tx_len; } -int rk628_mipi_dsi_generic_write(struct rk628 *rk628, - const void *payload, size_t size) +static int rk628_mipi_dsi_generic_write(struct rk628 *rk628, + const void *payload, size_t size) { const struct rk628_dsi *dsi = &rk628->dsi0; struct mipi_dsi_msg msg; @@ -731,8 +732,8 @@ int rk628_mipi_dsi_generic_write(struct rk628 *rk628, return rk628_dsi_transfer(rk628, dsi, &msg); } -int rk628_mipi_dsi_dcs_write_buffer(struct rk628 *rk628, - const void *data, size_t len) +static int rk628_mipi_dsi_dcs_write_buffer(struct rk628 *rk628, + const void *data, size_t len) { const struct rk628_dsi *dsi = &rk628->dsi0; struct mipi_dsi_msg msg; @@ -763,7 +764,8 @@ int rk628_mipi_dsi_dcs_write_buffer(struct rk628 *rk628, return rk628_dsi_transfer(rk628, dsi, &msg); } -int rk628_mipi_dsi_dcs_read(struct rk628 *rk628, u8 cmd, void *data, size_t len) +static __maybe_unused int rk628_mipi_dsi_dcs_read(struct rk628 *rk628, u8 cmd, + void *data, size_t len) { const struct rk628_dsi *dsi = &rk628->dsi0; struct mipi_dsi_msg msg; @@ -823,14 +825,20 @@ panel_simple_xfer_dsi_cmd_seq(struct rk628 *rk628, struct panel_cmds *cmds) static u32 rk628_dsi_get_lane_rate(const struct rk628_dsi *dsi) { const struct rk628_display_mode *mode = &dsi->rk628->dst_mode; - u32 lane_rate; + struct device_node *dsi_np; + u32 lane_rate, value; u32 max_lane_rate = 1500; u8 bpp, lanes; - bpp = dsi->bpp; - lanes = dsi->slave ? dsi->lanes * 2 : dsi->lanes; - lane_rate = mode->clock / 1000 * bpp / lanes; - lane_rate = DIV_ROUND_UP(lane_rate * 5, 4); + dsi_np = of_find_node_by_name(dsi->rk628->dev->of_node, "rk628-dsi"); + if (dsi_np && !of_property_read_u32(dsi_np, "rockchip,lane-mbps", &value)) { + lane_rate = value; + } else { + bpp = dsi->bpp; + lanes = dsi->slave ? dsi->lanes * 2 : dsi->lanes; + lane_rate = mode->clock / 1000 * bpp / lanes; + lane_rate = DIV_ROUND_UP(lane_rate * 5, 4); + } if (lane_rate > max_lane_rate) lane_rate = max_lane_rate; @@ -939,6 +947,65 @@ static void testif_write(struct rk628 *rk628, const struct rk628_dsi *dsi, dev_info(rk628->dev, "monitor_data: 0x%x\n", monitor_data); } +static void testif_set_timing(const struct rk628_dsi *dsi, u8 addr, + u8 max, u8 val) +{ + struct rk628 *rk628 = dsi->rk628; + + if (val > max) + return; + + testif_write(rk628, dsi, addr, (max + 1) | val); +} + +static void mipi_dphy_set_timing(const struct rk628_dsi *dsi) +{ + const struct { + unsigned int min_lane_mbps; + unsigned int max_lane_mbps; + u8 clk_lp; + u8 clk_hs_prepare; + u8 clk_hs_zero; + u8 clk_hs_trail; + u8 clk_post; + u8 data_lp; + u8 data_hs_prepare; + u8 data_hs_zero; + u8 data_hs_trail; + } timing_table[] = { + {800, 899, 0x07, 0x30, 0x25, 0x3c, 0x0f, 0x07, 0x40, 0x09, 0x40}, + {1100, 1249, 0x0a, 0x43, 0x2c, 0x50, 0x0f, 0x0a, 0x43, 0x10, 0x55}, + {1250, 1349, 0x0b, 0x43, 0x2c, 0x50, 0x0f, 0x0b, 0x53, 0x10, 0x5b}, + {1350, 1449, 0x0c, 0x43, 0x36, 0x60, 0x0f, 0x0c, 0x53, 0x10, 0x65}, + {1450, 1500, 0x0f, 0x60, 0x31, 0x60, 0x0f, 0x0e, 0x60, 0x11, 0x6a} + }; + unsigned int index; + + if (dsi->lane_mbps < timing_table[0].min_lane_mbps) + return; + + for (index = 0; index < ARRAY_SIZE(timing_table); index++) + if (dsi->lane_mbps >= timing_table[index].min_lane_mbps && + dsi->lane_mbps < timing_table[index].max_lane_mbps) + break; + + if (index == ARRAY_SIZE(timing_table)) + --index; + + if (dsi->lane_mbps < timing_table[index].max_lane_mbps) + return; + + testif_set_timing(dsi, 0x60, 0x3f, timing_table[index].clk_lp); + testif_set_timing(dsi, 0x61, 0x7f, timing_table[index].clk_hs_prepare); + testif_set_timing(dsi, 0x62, 0x3f, timing_table[index].clk_hs_zero); + testif_set_timing(dsi, 0x63, 0x7f, timing_table[index].clk_hs_trail); + testif_set_timing(dsi, 0x65, 0x0f, timing_table[index].clk_post); + testif_set_timing(dsi, 0x70, 0x3f, timing_table[index].data_lp); + testif_set_timing(dsi, 0x71, 0x7f, timing_table[index].data_hs_prepare); + testif_set_timing(dsi, 0x72, 0x3f, timing_table[index].data_hs_zero); + testif_set_timing(dsi, 0x73, 0x7f, timing_table[index].data_hs_trail); +} + static void mipi_dphy_init(struct rk628 *rk628, const struct rk628_dsi *dsi) { const struct { @@ -960,7 +1027,7 @@ static void mipi_dphy_init(struct rk628 *rk628, const struct rk628_dsi *dsi) unsigned int index; for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++) - if (lane_mbps <= hsfreqrange_table[index].max_lane_mbps) + if (dsi->lane_mbps <= hsfreqrange_table[index].max_lane_mbps) break; if (index == ARRAY_SIZE(hsfreqrange_table)) @@ -968,6 +1035,9 @@ static void mipi_dphy_init(struct rk628 *rk628, const struct rk628_dsi *dsi) hsfreqrange = hsfreqrange_table[index].hsfreqrange; testif_write(rk628, dsi, 0x44, HSFREQRANGE(hsfreqrange)); + + if (rk628->version == RK628F_VERSION) + mipi_dphy_set_timing(dsi); } static void mipi_dphy_power_on(struct rk628 *rk628, const struct rk628_dsi *dsi) @@ -1024,35 +1094,35 @@ static void mipi_dphy_power_on(struct rk628 *rk628, const struct rk628_dsi *dsi) udelay(10); } -void rk628_dsi0_reset_control_assert(struct rk628 *rk628) +static void rk628_dsi0_reset_control_assert(struct rk628 *rk628) { rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x400040); } -void rk628_dsi0_reset_control_deassert(struct rk628 *rk628) +static void rk628_dsi0_reset_control_deassert(struct rk628 *rk628) { rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x400000); } -void rk628_dsi1_reset_control_assert(struct rk628 *rk628) +static void rk628_dsi1_reset_control_assert(struct rk628 *rk628) { rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x800080); } -void rk628_dsi1_reset_control_deassert(struct rk628 *rk628) +static void rk628_dsi1_reset_control_deassert(struct rk628 *rk628) { rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x800000); } -void rk628_dsi_bridge_pre_enable(struct rk628 *rk628, - const struct rk628_dsi *dsi) +static void rk628_dsi_bridge_pre_enable(struct rk628 *rk628, + const struct rk628_dsi *dsi) { u32 val; dsi_write(rk628, dsi, DSI_PWR_UP, RESET); dsi_write(rk628, dsi, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE)); - val = DIV_ROUND_UP(lane_mbps >> 3, 20); + val = DIV_ROUND_UP(dsi->lane_mbps >> 3, 20); dsi_write(rk628, dsi, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) | TX_ESC_CLK_DIVISION(val)); @@ -1082,7 +1152,7 @@ static void rk628_dsi_set_vid_mode(struct rk628 *rk628, const struct rk628_dsi *dsi, const struct rk628_display_mode *mode) { - unsigned int lanebyteclk = (lane_mbps * 1000L) >> 3; + unsigned int lanebyteclk = (dsi->lane_mbps * 1000L) >> 3; unsigned int dpipclk = mode->clock; u32 hline, hsa, hbp, hline_time, hsa_time, hbp_time; u32 vactive, vsa, vfp, vbp; @@ -1149,9 +1219,22 @@ static void rk628_dsi_set_cmd_mode(struct rk628 *rk628, const struct rk628_dsi *dsi, const struct rk628_display_mode *mode) { + int cmd_size; + dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_LW_TX, 0); - dsi_write(rk628, dsi, DSI_EDPI_CMD_SIZE, - EDPI_ALLOWED_CMD_SIZE(mode->hdisplay)); + + /* rk628: The maximum capacity of dsi memory is 2048*32 bits */ + if (mode->hdisplay > 2048) + cmd_size = EDPI_ALLOWED_CMD_SIZE(mode->hdisplay / 2); + else + cmd_size = EDPI_ALLOWED_CMD_SIZE(mode->hdisplay); + + dsi_write(rk628, dsi, DSI_EDPI_CMD_SIZE, cmd_size); + + if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) + dsi_update_bits(rk628, dsi, DSI_LPCLK_CTRL, + AUTO_CLKLANE_CTRL, AUTO_CLKLANE_CTRL); + dsi_write(rk628, dsi, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE)); } @@ -1204,15 +1287,86 @@ static void rk628_dsi_bridge_enable(struct rk628 *rk628, dsi_write(rk628, dsi, DSI_PWR_UP, POWER_UP); } +static int rk628_dsi_color_bar_show(struct seq_file *s, void *data) +{ + seq_puts(s, " Enable color bar:\n"); + seq_puts(s, " example: echo 1 > /sys/kernel/debug/rk628/2-0050/dsi_color_bar\n"); + seq_puts(s, " Disable color bar:\n"); + seq_puts(s, " example: echo 0 > /sys/kernel/debug/rk628/2-0050/dsi_color_bar\n"); + + return 0; +} + +static int rk628_dsi_color_bar_open(struct inode *inode, struct file *file) +{ + return single_open(file, rk628_dsi_color_bar_show, inode->i_private); +} + +static ssize_t rk628_dsi_color_bar_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct rk628 *rk628 = ((struct seq_file *)file->private_data)->private; + struct rk628_dsi *dsi = &rk628->dsi0; + struct rk628_dsi *dsi1 = &rk628->dsi1; + u8 mode; + + if (kstrtou8_from_user(ubuf, len, 0, &mode)) + return -EFAULT; + + dsi_update_bits(rk628, dsi, DSI_VID_MODE_CFG, VPG_EN, mode ? VPG_EN : 0); + if (!mode) { + dsi_write(rk628, dsi, DSI_PWR_UP, RESET); + dsi_write(rk628, dsi, DSI_PWR_UP, POWER_UP); + } + + if (dsi->slave) { + dsi_update_bits(rk628, dsi1, DSI_VID_MODE_CFG, VPG_EN, mode ? VPG_EN : 0); + if (!mode) { + dsi_write(rk628, dsi1, DSI_PWR_UP, RESET); + dsi_write(rk628, dsi1, DSI_PWR_UP, POWER_UP); + } + } + + return len; +} + +static const struct file_operations rk628_dsi_color_bar_fops = { + .owner = THIS_MODULE, + .open = rk628_dsi_color_bar_open, + .read = seq_read, + .write = rk628_dsi_color_bar_write, + .llseek = seq_lseek, + .release = single_release, +}; + +void rk628_mipi_dsi_create_debugfs_file(struct rk628 *rk628) +{ + if (rk628_output_is_dsi(rk628)) + debugfs_create_file("dsi_color_bar", 0600, rk628->debug_dir, + rk628, &rk628_dsi_color_bar_fops); +} + void rk628_mipi_dsi_pre_enable(struct rk628 *rk628) { - const struct rk628_dsi *dsi = &rk628->dsi0; - const struct rk628_dsi *dsi1 = &rk628->dsi1; + struct rk628_dsi *dsi = &rk628->dsi0; + struct rk628_dsi *dsi1 = &rk628->dsi1; u32 rate = rk628_dsi_get_lane_rate(dsi); int bus_width; + u32 mask = SW_OUTPUT_MODE_MASK; + u32 val = SW_OUTPUT_MODE(OUTPUT_MODE_DSI); - rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_OUTPUT_MODE_MASK, - SW_OUTPUT_MODE(OUTPUT_MODE_DSI)); + if (rk628->version == RK628F_VERSION) { + mask = SW_OUTPUT_COMBTX_MODE_MASK; + val = SW_OUTPUT_COMBTX_MODE(OUTPUT_MODE_DSI - 2); + + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON3, + GRF_DPHY_CH1_EN_MASK | + GRF_AS_DSIPHY_MASK, + (dsi->slave ? GRF_DPHY_CH1_EN(1) : 0) | + GRF_AS_DSIPHY(1)); + } + + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, mask, val); rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, SW_SPLIT_EN, dsi->slave ? SW_SPLIT_EN : 0); @@ -1226,10 +1380,10 @@ void rk628_mipi_dsi_pre_enable(struct rk628 *rk628) rk628_combtxphy_set_bus_width(rk628, bus_width); rk628_combtxphy_set_mode(rk628, PHY_MODE_VIDEO_MIPI); - lane_mbps = rk628_combtxphy_get_bus_width(rk628); + dsi->lane_mbps = rk628_combtxphy_get_bus_width(rk628); - if (dsi->slave) - lane_mbps = rk628_combtxphy_get_bus_width(rk628); + if (dsi->slave && dsi1) + dsi1->lane_mbps = rk628_combtxphy_get_bus_width(rk628); /* rst for dsi0 */ rk628_dsi0_reset_control_assert(rk628); @@ -1262,7 +1416,7 @@ void rk628_mipi_dsi_pre_enable(struct rk628 *rk628) #endif dev_info(rk628->dev, "rk628_dsi final DSI-Link bandwidth: %d x %d\n", - lane_mbps, dsi->slave ? dsi->lanes * 2 : dsi->lanes); + dsi->lane_mbps, dsi->slave ? dsi->lanes * 2 : dsi->lanes); } void rk628_mipi_dsi_enable(struct rk628 *rk628) diff --git a/drivers/misc/rk628/rk628_dsi.h b/drivers/misc/rk628/rk628_dsi.h index 7753b49e747d..9627cbad2c02 100644 --- a/drivers/misc/rk628/rk628_dsi.h +++ b/drivers/misc/rk628/rk628_dsi.h @@ -155,4 +155,5 @@ int rk628_dsi_parse(struct rk628 *rk628, struct device_node *dsi_np); void rk628_mipi_dsi_pre_enable(struct rk628 *rk628); void rk628_mipi_dsi_enable(struct rk628 *rk628); void rk628_dsi_disable(struct rk628 *rk628); +void rk628_mipi_dsi_create_debugfs_file(struct rk628 *rk628); #endif diff --git a/drivers/misc/rk628/rk628_efuse.c b/drivers/misc/rk628/rk628_efuse.c new file mode 100644 index 000000000000..ee41b0d0fb72 --- /dev/null +++ b/drivers/misc/rk628/rk628_efuse.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RK628 eFuse Driver + * + * Copyright (c) 2020 Rockchip Electronics Co. Ltd. + * + * Author: Weixin Zhou + */ + +#include +#include "rk628.h" + +#define EFUSE_SIZE 64 + +#define T_CSB_P_S 0 +#define T_PGENB_P_S (15 + 200) +#define T_LOAD_P_S 0 +#define T_ADDR_P_S (15 + 200 + 5) +#define T_STROBE_P_S ((150 + 2000 + 100) / 9) +#define T_CSB_P_L 0 +#define T_PGENB_P_L (15 + 200 + 10 + 200 + 190 + 10) +#define T_LOAD_P_L (15 + 200 + 200 + 190 + 10 + 100 + 15) +#define T_ADDR_P_L (15 + 200 + 5 + 200 + 5) +#define T_STROBE_P_L ((150 + 2000 + 100 + 2000) / 9) +#define T_CSB_R_S 0 +#define T_PGENB_R_S 0 +#define T_LOAD_R_S 15 +#define T_ADDR_R_S (15 + 9) +#define T_STROBE_R_S ((150 + 100) / 9) +#define T_CSB_R_L 0 +#define T_PGENB_R_L 0 +#define T_LOAD_R_L (15 + 5 + 5 + 10 + 15) +#define T_ADDR_R_L (15 + 10 + 5 + 1) +#define T_STROBE_R_L ((150 + 100 + 50) / 8) + +#define T_CSB_P 0x28 +#define T_PGENB_P 0x2c +#define T_LOAD_P 0x30 +#define T_ADDR_P 0x34 +#define T_STROBE_P 0x38 +#define T_CSB_R 0x3c +#define T_PGENB_R 0x40 +#define T_LOAD_R 0x44 +#define T_ADDR_R 0x48 +#define T_STROBE_R 0x4c + +#define RK628_EFUSE_BASE 0xb0000 +#define RK628_MOD 0x00 +#define RK628_INT_STATUS 0x0018 +#define RK628_DOUT 0x0020 +#define RK628_AUTO_CTRL 0x0024 +#define RK628_USER_MODE BIT(0) +#define RK628_INT_FINISH BIT(0) +#define RK628_AUTO_ENB BIT(0) +#define RK628_AUTO_RD BIT(1) +#define RK628_ADDR_ROW 16 +#define RK628_ADDR_COL 22 +#define RK628_A_SHIFT 16 +#define RK628_A_MASK 0x3ff +#define RK628_NBYTES 1 + +#define REG_EFUSE_CTRL 0x0000 +#define REG_EFUSE_DOUT 0x0004 + +static inline u32 rk628_read(struct rk628 *rk628, u32 reg) +{ + u32 val; + + rk628_i2c_read(rk628, reg, &val); + + return val; +} + +static inline void rk628_write(struct rk628 *rk628, u32 val, u32 reg) +{ + rk628_i2c_write(rk628, reg, val); +} + +static inline void rk628_efuse_timing_init(struct rk628 *rk628) +{ + u32 base = RK628_EFUSE_BASE; + /* enable auto mode */ + rk628_write(rk628, rk628_read(rk628, base + RK628_MOD) + & (~RK628_USER_MODE), base + RK628_MOD); + + /* setup efuse timing */ + rk628_write(rk628, (T_CSB_P_S << 16) | T_CSB_P_L, base + T_CSB_P); + rk628_write(rk628, (T_PGENB_P_S << 16) | T_PGENB_P_L, base + T_PGENB_P); + rk628_write(rk628, (T_LOAD_P_S << 16) | T_LOAD_P_L, base + T_LOAD_P); + rk628_write(rk628, (T_ADDR_P_S << 16) | T_ADDR_P_L, base + T_ADDR_P); + rk628_write(rk628, (T_STROBE_P_S << 16) | T_STROBE_P_L, base + T_STROBE_P); + rk628_write(rk628, (T_CSB_R_S << 16) | T_CSB_R_L, base + T_CSB_R); + rk628_write(rk628, (T_PGENB_R_S << 16) | T_PGENB_R_L, base + T_PGENB_R); + rk628_write(rk628, (T_LOAD_R_S << 16) | T_LOAD_R_L, base + T_LOAD_R); + rk628_write(rk628, (T_ADDR_R_S << 16) | T_ADDR_R_L, base + T_ADDR_R); + rk628_write(rk628, (T_STROBE_R_S << 16) | T_STROBE_R_L, base + T_STROBE_R); +} + +static inline void rk628_efuse_timing_deinit(struct rk628 *rk628) +{ + u32 base = RK628_EFUSE_BASE; + /* disable auto mode */ + rk628_write(rk628, rk628_read(rk628, base + RK628_MOD) + | RK628_USER_MODE, base + RK628_MOD); + + /* clear efuse timing */ + rk628_write(rk628, 0, base + T_CSB_P); + rk628_write(rk628, 0, base + T_PGENB_P); + rk628_write(rk628, 0, base + T_LOAD_P); + rk628_write(rk628, 0, base + T_ADDR_P); + rk628_write(rk628, 0, base + T_STROBE_P); + rk628_write(rk628, 0, base + T_CSB_R); + rk628_write(rk628, 0, base + T_PGENB_R); + rk628_write(rk628, 0, base + T_LOAD_R); + rk628_write(rk628, 0, base + T_ADDR_R); + rk628_write(rk628, 0, base + T_STROBE_R); +} + +int rk628_efuse_read(struct rk628 *rk628, unsigned int offset, + void *val, size_t bytes) +{ + unsigned int addr_start, addr_end, addr_offset, addr_len; + u32 out_value, status; + u8 *buf; + int ret = 0, i = 0; + + addr_start = rounddown(offset, RK628_NBYTES) / RK628_NBYTES; + addr_end = roundup(offset + bytes, RK628_NBYTES) / RK628_NBYTES; + addr_offset = offset % RK628_NBYTES; + addr_len = addr_end - addr_start; + + if (addr_len == 0 || addr_len > EFUSE_SIZE) + return -EINVAL; + + buf = kzalloc(sizeof(*buf) * addr_len * RK628_NBYTES, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + rk628_efuse_timing_init(rk628); + + while (addr_len--) { + rk628_write(rk628, RK628_AUTO_RD | RK628_AUTO_ENB | + ((addr_start++ & RK628_A_MASK) << RK628_A_SHIFT), + RK628_EFUSE_BASE + RK628_AUTO_CTRL); + udelay(2); + status = rk628_read(rk628, RK628_EFUSE_BASE + RK628_INT_STATUS); + if (!(status & RK628_INT_FINISH)) { + ret = -EIO; + goto err; + } + out_value = rk628_read(rk628, RK628_EFUSE_BASE + RK628_DOUT); + rk628_write(rk628, RK628_INT_FINISH, RK628_EFUSE_BASE + RK628_INT_STATUS); + + memcpy(&buf[i], &out_value, RK628_NBYTES); + i += RK628_NBYTES; + } + memcpy(val, buf + addr_offset, bytes); +err: + rk628_efuse_timing_deinit(rk628); + kfree(buf); + + return ret; +} diff --git a/drivers/misc/rk628/rk628_efuse.h b/drivers/misc/rk628/rk628_efuse.h new file mode 100644 index 000000000000..dcd7ac5abc67 --- /dev/null +++ b/drivers/misc/rk628/rk628_efuse.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 Rockchip Electronics Co. Ltd. + * + * Author: Weixin Zhou + */ + +#ifndef RK628_EFUSE_H +#define RK628_EFUSE_H + +#define EFUSE_BASE 0xb0000 +#define EFUSE_REVISION 0x50 + +int rk628_efuse_read(struct rk628 *rk628, unsigned int offset, + void *val, size_t bytes); + +#endif diff --git a/drivers/misc/rk628/rk628_gvi.c b/drivers/misc/rk628/rk628_gvi.c index 7539cb7d7054..fb41c2143bb5 100644 --- a/drivers/misc/rk628/rk628_gvi.c +++ b/drivers/misc/rk628/rk628_gvi.c @@ -5,6 +5,7 @@ * Author: Guochun Huang */ +#include #include "linux/printk.h" #include "rk628.h" #include "rk628_config.h" @@ -12,6 +13,8 @@ #include "rk628_gvi.h" #include "panel.h" +#define GVI_RETRY_TIMEOUT 10 + int rk628_gvi_parse(struct rk628 *rk628, struct device_node *gvi_np) { const char *string; @@ -21,8 +24,6 @@ int rk628_gvi_parse(struct rk628 *rk628, struct device_node *gvi_np) if (!of_device_is_available(gvi_np)) return -EINVAL; - rk628->output_mode = OUTPUT_MODE_GVI; - if (!of_property_read_u32(gvi_np, "gvi,lanes", &val)) rk628->gvi.lanes = val; @@ -89,10 +90,17 @@ static void rk628_gvi_get_info(struct rk628_gvi *gvi) static unsigned int rk628_gvi_get_lane_rate(struct rk628 *rk628) { - const struct rk628_display_mode *mode = &rk628->dst_mode; struct rk628_gvi *gvi = &rk628->gvi; u32 lane_bit_rate, min_lane_rate = 500000, max_lane_rate = 4000000; u64 total_bw; + struct rk628_display_mode *src = &rk628->src_mode; + const struct rk628_display_mode *dst = &rk628->dst_mode; + u64 dst_rate, src_rate; + + src_rate = src->clock * 1000; + dst_rate = src_rate * dst->vtotal * dst->htotal; + do_div(dst_rate, (src->vtotal * src->htotal)); + do_div(dst_rate, 1000); /** * [ENCODER TOTAL BIT-RATE](bps) = [byte mode](byte) x 10 / [pixel clock](HZ) @@ -101,7 +109,7 @@ static unsigned int rk628_gvi_get_lane_rate(struct rk628 *rk628) * * 500Mbps <= lane_bit_rate <= 4Gbps */ - total_bw = (u64)gvi->byte_mode * 10 * mode->clock;/* Kbps */ + total_bw = (u64)gvi->byte_mode * 10 * dst_rate;/* Kbps */ do_div(total_bw, gvi->lanes); lane_bit_rate = total_bw; @@ -122,6 +130,8 @@ static void rk628_gvi_pre_enable(struct rk628 *rk628, struct rk628_gvi *gvi) rk628_i2c_update_bits(rk628, GVI_SYS_RST, SYS_RST_SOFT_RST, 0); udelay(10); + rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_8_PIXEL_ALIGN(1)); + rk628_i2c_update_bits(rk628, GVI_SYS_CTRL0, SYS_CTRL0_LANE_NUM_MASK, SYS_CTRL0_LANE_NUM(gvi->lanes - 1)); rk628_i2c_update_bits(rk628, GVI_SYS_CTRL0, SYS_CTRL0_BYTE_MODE_MASK, @@ -137,6 +147,9 @@ static void rk628_gvi_pre_enable(struct rk628 *rk628, struct rk628_gvi *gvi) rk628_i2c_update_bits(rk628, GVI_SYS_CTRL0, SYS_CTRL0_FRM_RST_EN, gvi->frm_rst ? SYS_CTRL0_FRM_RST_EN : 0); rk628_i2c_update_bits(rk628, GVI_SYS_CTRL1, SYS_CTRL1_LANE_ALIGN_EN, 0); + + rk628_i2c_update_bits(rk628, GVI_SYS_CTRL1, SYS_CTRL1_COLOR_DEPTH_MASK, + SYS_CTRL1_COLOR_DEPTH(gvi->color_depth)); } static void rk628_gvi_enable_color_bar(struct rk628 *rk628, @@ -183,6 +196,50 @@ static void rk628_gvi_enable_color_bar(struct rk628 *rk628, rk628_i2c_update_bits(rk628, GVI_COLOR_BAR_CTRL, COLOR_BAR_EN, 0); } +static int rk628_gvi_color_bar_show(struct seq_file *s, void *data) +{ + seq_puts(s, " Enable color bar:\n"); + seq_puts(s, " example: echo 1 > /sys/kernel/debug/rk628/2-0050/gvi_color_bar\n"); + seq_puts(s, " Disable color bar:\n"); + seq_puts(s, " example: echo 0 > /sys/kernel/debug/rk628/2-0050/gvi_color_bar\n"); + + return 0; +} + +static int rk628_gvi_color_bar_open(struct inode *inode, struct file *file) +{ + return single_open(file, rk628_gvi_color_bar_show, inode->i_private); +} + +static ssize_t rk628_gvi_color_bar_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct rk628 *rk628 = ((struct seq_file *)file->private_data)->private; + u8 mode; + + if (kstrtou8_from_user(ubuf, len, 0, &mode)) + return -EFAULT; + + rk628_i2c_update_bits(rk628, GVI_COLOR_BAR_CTRL, COLOR_BAR_EN, mode ? 1 : 0); + + return len; +} + +static const struct file_operations rk628_gvi_color_bar_fops = { + .owner = THIS_MODULE, + .open = rk628_gvi_color_bar_open, + .read = seq_read, + .write = rk628_gvi_color_bar_write, + .llseek = seq_lseek, + .release = single_release, +}; + +void rk628_gvi_create_debugfs_file(struct rk628 *rk628) +{ + if (rk628_output_is_gvi(rk628)) + debugfs_create_file("gvi_color_bar", 0600, rk628->debug_dir, + rk628, &rk628_gvi_color_bar_fops); +} static void rk628_gvi_post_enable(struct rk628 *rk628, struct rk628_gvi *gvi) { @@ -196,14 +253,22 @@ void rk628_gvi_enable(struct rk628 *rk628) { struct rk628_gvi *gvi = &rk628->gvi; unsigned int rate; + u32 mask = SW_OUTPUT_MODE_MASK; + u32 val = SW_OUTPUT_MODE(OUTPUT_MODE_GVI); + int i = 0; rk628_gvi_get_info(gvi); rate = rk628_gvi_get_lane_rate(rk628); /* set gvi_hpd and gvi_lock mux */ rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x06000600); - rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_OUTPUT_MODE_MASK, - SW_OUTPUT_MODE(OUTPUT_MODE_GVI)); + + if (rk628->version == RK628F_VERSION) { + mask = SW_OUTPUT_COMBTX_MODE_MASK; + val = SW_OUTPUT_COMBTX_MODE(OUTPUT_MODE_GVI); + } + + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, mask, val); rk628_combtxphy_set_bus_width(rk628, rate); rk628_combtxphy_set_gvi_division_mode(rk628, gvi->division_mode); rk628_combtxphy_set_mode(rk628, PHY_MODE_VIDEO_GVI); @@ -213,7 +278,30 @@ void rk628_gvi_enable(struct rk628 *rk628) rk628_panel_prepare(rk628); rk628_gvi_enable_color_bar(rk628, gvi); rk628_gvi_post_enable(rk628, gvi); + + for (i = 0; i < 100; i++) { + rk628_i2c_read(rk628, GVI_STATUS, &val); + if ((val & GVI_LOCKED_MASK) == GVI_LOCKED_STATUS) + break; + usleep_range(1000, 1100); + } + + if (i == 100 && gvi->retry_times < GVI_RETRY_TIMEOUT) { + dev_info(rk628->dev, "GVI Lock failed: 0x%x, try again: %d\n", val, gvi->retry_times); + gvi->retry_times++; + rk628_gvi_disable(rk628); + usleep_range(50000, 51000); + rk628_gvi_enable(rk628); + } + + if (gvi->retry_times >= GVI_RETRY_TIMEOUT && i == 100) { + dev_info(rk628->dev, "GVI Lock failed, please check hardware!\n"); + return; + } rk628_panel_enable(rk628); + + if (i != 100) + gvi->retry_times = 0; dev_info(rk628->dev, "GVI-Link bandwidth: %d x %d Mbps, Byte mode: %d, Color Depty: %d, %s division mode\n", rate, gvi->lanes, gvi->byte_mode, gvi->color_depth, diff --git a/drivers/misc/rk628/rk628_gvi.h b/drivers/misc/rk628/rk628_gvi.h index 451d0cd0edd2..2cfc236ea1d1 100644 --- a/drivers/misc/rk628/rk628_gvi.h +++ b/drivers/misc/rk628/rk628_gvi.h @@ -20,6 +20,8 @@ #define GVI_SYS_RST HOSTREG(0x0014) #define GVI_LINE_FLAG HOSTREG(0x0018) #define GVI_STATUS HOSTREG(0x001c) +#define GVI_LOCKED_MASK 0x70 +#define GVI_LOCKED_STATUS 0x40 #define GVI_PLL_LOCK_TIMEOUT HOSTREG(0x0030) #define GVI_HTPDN_TIMEOUT HOSTREG(0x0034) #define GVI_LOCKN_TIMEOUT HOSTREG(0x0038) @@ -214,5 +216,5 @@ int rk628_gvi_parse(struct rk628 *rk628, struct device_node *gvi_np); void rk628_gvi_enable(struct rk628 *rk628); void rk628_gvi_disable(struct rk628 *rk628); - +void rk628_gvi_create_debugfs_file(struct rk628 *rk628); #endif diff --git a/drivers/misc/rk628/rk628_hdmirx.c b/drivers/misc/rk628/rk628_hdmirx.c index bfb8866f77e3..5667f10b8356 100644 --- a/drivers/misc/rk628/rk628_hdmirx.c +++ b/drivers/misc/rk628/rk628_hdmirx.c @@ -4,12 +4,14 @@ * * Author: Chen Shunqing */ +#include #include #include #include "rk628.h" #include "rk628_combrxphy.h" #include "rk628_config.h" +#include "rk628_cru.h" #include "rk628_hdmirx.h" #define POLL_INTERVAL_MS 1000 @@ -20,40 +22,23 @@ static u8 debug; static u8 edid_init_data[] = { - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, - 0x49, 0x78, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x12, 0x1E, 0x01, 0x03, 0x80, 0x00, 0x00, 0x78, - 0x0A, 0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27, - 0x12, 0x48, 0x4C, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A, - 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, - 0x45, 0x00, 0xC4, 0x8E, 0x21, 0x00, 0x00, 0x1E, - 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x50, - 0x72, 0x6F, 0x6A, 0x65, 0x63, 0x74, 0x6F, 0x72, - 0x0A, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFD, - 0x00, 0x14, 0x78, 0x01, 0xFF, 0x1D, 0x00, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x18, - - 0x02, 0x03, 0x13, 0x71, 0x40, 0x23, 0x09, 0x07, - 0x01, 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0C, - 0x00, 0x10, 0x00, 0x02, 0x3A, 0x80, 0x18, 0x71, - 0x38, 0x2D, 0x40, 0x58, 0x2C, 0x45, 0x00, 0x20, - 0xC2, 0x31, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, +0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x49, 0x78, 0x28, 0x06, 0x01, 0x00, 0x00, 0x00, +0xFF, 0x21, 0x01, 0x03, 0x80, 0x8B, 0x4E, 0x78, 0x2A, 0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27, +0x12, 0x48, 0x4C, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, +0x45, 0x00, 0x20, 0xC2, 0x31, 0x00, 0x00, 0x1E, 0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20, +0x6E, 0x28, 0x55, 0x00, 0x20, 0xC2, 0x31, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x52, +0x4B, 0x36, 0x32, 0x38, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFD, +0x00, 0x17, 0x4C, 0x0F, 0x50, 0x1E, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xB4, +0x02, 0x03, 0x32, 0xF0, 0x41, 0x61, 0x23, 0x09, 0x17, 0x07, 0x83, 0x47, 0x00, 0x00, 0x6E, 0x03, +0x0C, 0x00, 0x20, 0x00, 0xB8, 0x3C, 0x20, 0x00, 0x80, 0x01, 0x02, 0x03, 0x04, 0x67, 0xD8, 0x5D, +0xC4, 0x01, 0x78, 0x80, 0x01, 0xE5, 0x0F, 0x01, 0x00, 0x00, 0x00, 0xE3, 0x05, 0xC3, 0x01, 0xE2, +0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, }; struct rk628_hdmi_mode { @@ -69,6 +54,28 @@ struct rk628_hdmi_mode { unsigned int flags; }; +#define INIT_FIFO_STATE 64 + +#define is_validfs(x) (x == 32000 || \ + x == 44100 || \ + x == 48000 || \ + x == 88200 || \ + x == 96000 || \ + x == 176400 || \ + x == 192000 || \ + x == 768000) + +struct rk628_audiostate { + u32 hdmirx_aud_clkrate; + u32 fs_audio; + u32 ctsn_flag; + u32 fifo_flag; + int init_state; + int pre_state; + bool fifo_int; + bool audio_enable; +}; + struct rk628_hdmirx { bool plugin; bool res_change; @@ -78,17 +85,347 @@ struct rk628_hdmirx { bool audio_present; bool hpd_output_inverted; bool src_mode_4K_yuv420; + bool src_depth_10bit; bool phy_lock; + bool is_hdmi2; + struct rk628 *rk628; + struct delayed_work delayed_work_audio; + struct rk628_audiostate audio_state; }; +void rk628_hdmirx_reset_control_assert(struct rk628 *rk628) +{ + /* presetn_hdmirx */ + rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x40004); + /* resetn_hdmirx_pon */ + rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x10001000); +} + +void rk628_hdmirx_reset_control_deassert(struct rk628 *rk628) +{ + /* presetn_hdmirx */ + rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x40000); + /* resetn_hdmirx_pon */ + rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x10000000); +} + +static void hdmirx_phy_write(struct rk628 *rk628, u32 offset, u32 val) +{ + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_ADDRESS, offset); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_DATAO, val); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_OPERATION, 1); +} + +static u32 hdmirx_phy_read(struct rk628 *rk628, u32 offset) +{ + u32 val; + + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_ADDRESS, offset); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_OPERATION, 2); + rk628_i2c_read(rk628, HDMI_RX_I2CM_PHYG3_DATAI, &val); + + return val; +} + +void rk628_hdmirx_phy_enable(struct rk628 *rk628, bool is_hdmi2) +{ + hdmirx_phy_write(rk628, 0x02, 0x1860); + hdmirx_phy_write(rk628, 0x03, 0x0060); + hdmirx_phy_write(rk628, 0x27, 0x1c94); + hdmirx_phy_write(rk628, 0x28, 0x3713); + hdmirx_phy_write(rk628, 0x29, 0x24da); + hdmirx_phy_write(rk628, 0x2a, 0x5492); + hdmirx_phy_write(rk628, 0x2b, 0x4b0d); + hdmirx_phy_write(rk628, 0x2d, 0x008c); + hdmirx_phy_write(rk628, 0x2e, 0x0001); + + if (is_hdmi2) + hdmirx_phy_write(rk628, 0x0e, 0x0108); + else + hdmirx_phy_write(rk628, 0x0e, 0x0008); +} + +static int rk628_hdmirx_phy_show(struct seq_file *s, void *v) +{ + struct rk628 *rk628 = s->private; + u32 i = 0; + + seq_puts(s, "\n>>>rxphy reg "); + for (i = 0; i <= 0xb7; i++) + seq_printf(s, "regs %02x val %08x\n", + i, hdmirx_phy_read(rk628, i)); + + return 0; +} + +static int rk628_hdmirx_phy_open(struct inode *inode, struct file *file) +{ + return single_open(file, rk628_hdmirx_phy_show, inode->i_private); +} + +static ssize_t +rk628_hdmirx_phy_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct rk628 *rk628 = + ((struct seq_file *)file->private_data)->private; + u32 reg, val; + char kbuf[25]; + + if (count > 24) { + dev_err(rk628->dev, "out of buf range\n"); + return count; + } + + if (copy_from_user(kbuf, buf, count)) + return -EFAULT; + kbuf[count - 1] = '\0'; + + if (sscanf(kbuf, "%x %x", ®, &val) == -1) + return -EFAULT; + if (reg > 0xb7) { + dev_err(rk628->dev, "it is no a rxphy register\n"); + return count; + } + dev_info(rk628->dev, "/**********rxphy register config******/"); + dev_info(rk628->dev, "\n reg=%x val=%x\n", reg, val); + hdmirx_phy_write(rk628, reg, val); + + return count; +} + +static const struct file_operations rk628_hdmirx_phy_fops = { + .owner = THIS_MODULE, + .open = rk628_hdmirx_phy_open, + .read = seq_read, + .write = rk628_hdmirx_phy_write, + .llseek = seq_lseek, + .release = single_release, +}; + +void rk628_hdmirx_phy_set_clrdpt(struct rk628 *rk628, bool is_8bit) +{ + if (is_8bit) + hdmirx_phy_write(rk628, 0x03, 0x0000); + else + hdmirx_phy_write(rk628, 0x03, 0x0060); +} + +static uint32_t rk628_hdmirx_audio_fs(struct rk628 *rk628) +{ + u64 tmdsclk = 0; + u32 clkrate = 0, cts_decoded = 0, n_decoded = 0, fs_audio = 0; + + /* fout=128*fs=ftmds*N/CTS */ + rk628_i2c_read(rk628, HDMI_RX_HDMI_CKM_RESULT, &clkrate); + clkrate = clkrate & 0xffff; + /* tmdsclk = (clkrate/1000) * 49500000 */ + tmdsclk = clkrate * (49500000 / 1000); + rk628_i2c_read(rk628, HDMI_RX_PDEC_ACR_CTS, &cts_decoded); + rk628_i2c_read(rk628, HDMI_RX_PDEC_ACR_N, &n_decoded); + if (cts_decoded != 0) { + fs_audio = div_u64((tmdsclk * n_decoded), cts_decoded); + fs_audio /= 128; + fs_audio = div_u64(fs_audio + 50, 100); + fs_audio *= 100; + } + dev_dbg(rk628->dev, + "%s: clkrate:%u tmdsclk:%llu, n_decoded:%u, cts_decoded:%u, fs_audio:%u\n", + __func__, clkrate, tmdsclk, n_decoded, cts_decoded, fs_audio); + if (!is_validfs(fs_audio)) + fs_audio = 0; + return fs_audio; +} + +static void rk628_hdmirx_audio_clk_set_rate(struct rk628_hdmirx *hdmirx, u32 rate) +{ + dev_dbg(hdmirx->rk628->dev, "%s: %u to %u\n", + __func__, hdmirx->audio_state.hdmirx_aud_clkrate, rate); + rk628_cru_clk_set_rate(hdmirx->rk628, CGU_CLK_HDMIRX_AUD, rate); + hdmirx->audio_state.hdmirx_aud_clkrate = rate; +} + +static void rk628_hdmirx_audio_clk_ppm_inc(struct rk628_hdmirx *hdmirx, int ppm) +{ + int delta, rate, inc; + + rate = hdmirx->audio_state.hdmirx_aud_clkrate; + if (ppm < 0) { + ppm = -ppm; + inc = -1; + } else + inc = 1; + delta = (uint64_t)((uint64_t)rate * ppm + 500000) / 1000000; + delta *= inc; + rate += delta; + dev_dbg(hdmirx->rk628->dev, "%s: %u to %u(delta:%d)\n", + __func__, hdmirx->audio_state.hdmirx_aud_clkrate, rate, delta); + rk628_cru_clk_set_rate(hdmirx->rk628, CGU_CLK_HDMIRX_AUD, rate); + hdmirx->audio_state.hdmirx_aud_clkrate = rate; +} + +static void rk628_hdmirx_audio_fifo_init(struct rk628_hdmirx *hdmirx) +{ + rk628_i2c_write(hdmirx->rk628, HDMI_RX_AUD_FIFO_ICLR, 0x1f); + rk628_i2c_write(hdmirx->rk628, HDMI_RX_AUD_FIFO_CTRL, 0x10001); + //delay_us(100); + rk628_i2c_write(hdmirx->rk628, HDMI_RX_AUD_FIFO_CTRL, 0x10000); +} + +static void rk628_hdmirx_audio_set_fs(struct rk628_hdmirx *hdmirx, u32 fs_audio) +{ + u32 hdmirx_aud_clkrate_t = fs_audio*128; + + dev_dbg(hdmirx->rk628->dev, "%s: %u to %u with fs %u\n", __func__, + hdmirx->audio_state.hdmirx_aud_clkrate, hdmirx_aud_clkrate_t, + fs_audio); + rk628_hdmirx_audio_clk_set_rate(hdmirx, hdmirx_aud_clkrate_t); + hdmirx->audio_state.fs_audio = fs_audio; +} + +static const char *audio_fifo_err(u32 fifo_status) +{ + switch (fifo_status & (AFIF_UNDERFL_ISTS | AFIF_OVERFL_ISTS)) { + case AFIF_UNDERFL_ISTS: + return "underflow"; + case AFIF_OVERFL_ISTS: + return "overflow"; + case AFIF_UNDERFL_ISTS | AFIF_OVERFL_ISTS: + return "underflow and overflow"; + } + return "underflow or overflow"; +} + +static void rk628_hdmirx_delayed_work_audio(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct rk628_hdmirx *hdmirx = container_of(dwork, struct rk628_hdmirx, + delayed_work_audio); + struct rk628 *rk628 = hdmirx->rk628; + struct rk628_audiostate *audio_state = &hdmirx->audio_state; + u32 fs_audio; + int init_state, pre_state, fifo_status, fifo_ints; + unsigned long delay = 500; + + fs_audio = rk628_hdmirx_audio_fs(rk628); + /* read fifo init status */ + rk628_i2c_read(rk628, HDMI_RX_AUD_FIFO_ISTS, &fifo_ints); + dev_dbg(rk628->dev, "%s: HDMI_RX_AUD_FIFO_ISTS:%#x\r\n", __func__, fifo_ints); + + if (fifo_ints & (AFIF_UNDERFL_ISTS | AFIF_OVERFL_ISTS)) { + dev_warn(rk628->dev, "%s: audio %s %#x, with fs %svalid %d\n", + __func__, audio_fifo_err(fifo_ints), fifo_ints, + is_validfs(fs_audio) ? "" : "in", fs_audio); + if (is_validfs(fs_audio)) + rk628_hdmirx_audio_set_fs(hdmirx, fs_audio); + rk628_hdmirx_audio_fifo_init(hdmirx); + audio_state->pre_state = 0; + goto exit; + } + + /* read fifo fill status */ + init_state = audio_state->init_state; + pre_state = audio_state->pre_state; + rk628_i2c_read(rk628, HDMI_RX_AUD_FIFO_FILLSTS1, &fifo_status); + dev_dbg(rk628->dev, + "%s: HDMI_RX_AUD_FIFO_FILLSTS1:%#x, single offset:%d, total offset:%d\n", + __func__, fifo_status, fifo_status - pre_state, fifo_status - init_state); + if (!is_validfs(fs_audio)) { + dev_dbg(rk628->dev, + "%s: no supported fs(%u), fifo_status %d\n", + __func__, fs_audio, fifo_status); + delay = 1000; + } else if (abs(fs_audio - audio_state->fs_audio) > 1000) { + dev_info(rk628->dev, "%s: restart audio fs(%d -> %d)\n", + __func__, audio_state->fs_audio, fs_audio); + rk628_hdmirx_audio_set_fs(hdmirx, fs_audio); + rk628_hdmirx_audio_fifo_init(hdmirx); + audio_state->pre_state = 0; + goto exit; + } + if (fifo_status != 0) { + if (!hdmirx->audio_present) { + dev_info(rk628->dev, "audio on"); + hdmirx->audio_present = true; + } + if (fifo_status - init_state > 16 && fifo_status - pre_state > 0) + rk628_hdmirx_audio_clk_ppm_inc(hdmirx, 10); + else if (fifo_status - init_state < -16 && fifo_status - pre_state < 0) + rk628_hdmirx_audio_clk_ppm_inc(hdmirx, -10); + } else { + if (hdmirx->audio_present) { + dev_info(rk628->dev, "audio off"); + hdmirx->audio_present = false; + } + } + audio_state->pre_state = fifo_status; +exit: + schedule_delayed_work(&hdmirx->delayed_work_audio, msecs_to_jiffies(delay)); +} + +static void rk628_hdmirx_audio_setup(struct rk628 *rk628) +{ + u32 audio_pll_n, audio_pll_cts; + struct rk628_hdmirx *hdmirx = rk628->hdmirx; + + audio_pll_n = 5644; + audio_pll_cts = 148500; + hdmirx->audio_state.fs_audio = 0; + hdmirx->audio_state.pre_state = 0; + hdmirx->audio_state.init_state = INIT_FIFO_STATE*4; + rk628_hdmirx_audio_set_fs(hdmirx, 44100); + /* manual aud CTS */ + rk628_i2c_write(rk628, HDMI_RX_AUDPLL_GEN_CTS, audio_pll_cts); + /* manual aud N */ + rk628_i2c_write(rk628, HDMI_RX_AUDPLL_GEN_N, audio_pll_n); + + /* aud CTS N en manual */ + rk628_i2c_update_bits(rk628, HDMI_RX_AUD_CLK_CTRL, + CTS_N_REF_MASK, CTS_N_REF(1)); + /* aud pll ctrl */ + rk628_i2c_update_bits(rk628, HDMI_RX_AUD_PLL_CTRL, + PLL_LOCK_TOGGLE_DIV_MASK, PLL_LOCK_TOGGLE_DIV(0)); + rk628_i2c_update_bits(rk628, HDMI_RX_AUD_FIFO_TH, + AFIF_TH_START_MASK | + AFIF_TH_MAX_MASK | + AFIF_TH_MIN_MASK, + AFIF_TH_START(INIT_FIFO_STATE) | + AFIF_TH_MAX(8) | + AFIF_TH_MIN(8)); + + /* AUTO_VMUTE */ + rk628_i2c_update_bits(rk628, HDMI_RX_AUD_FIFO_CTRL, + AFIF_SUBPACKET_DESEL_MASK | + AFIF_SUBPACKETS_MASK, + AFIF_SUBPACKET_DESEL(0) | + AFIF_SUBPACKETS(1)); + rk628_i2c_write(rk628, HDMI_RX_AUD_SAO_CTRL, + I2S_LPCM_BPCUV(0) | + I2S_32_16(1)); + rk628_i2c_write(rk628, HDMI_RX_AUD_MUTE_CTRL, + APPLY_INT_MUTE(0) | + APORT_SHDW_CTRL(3) | + AUTO_ACLK_MUTE(2) | + AUD_MUTE_SPEED(1) | + AUD_AVMUTE_EN(1) | + AUD_MUTE_SEL(0) | + AUD_MUTE_MODE(1)); + rk628_i2c_write(rk628, HDMI_RX_AUD_PAO_CTRL, PAO_RATE(0)); + rk628_i2c_write(rk628, HDMI_RX_AUD_CHEXTR_CTRL, AUD_LAYOUT_CTRL(1)); + /* audio detect */ + rk628_i2c_write(rk628, HDMI_RX_PDEC_AUDIODET_CTRL, AUDIODET_THRESHOLD(0)); + schedule_delayed_work(&hdmirx->delayed_work_audio, msecs_to_jiffies(1000)); +} + static void rk628_hdmirx_ctrl_enable(struct rk628 *rk628) { - rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_INPUT_MODE_MASK, SW_INPUT_MODE(INPUT_MODE_HDMI)); - rk628_i2c_write(rk628, HDMI_RX_HDMI20_CONTROL, 0x10001f10); + rk628_i2c_write(rk628, HDMI_RX_HDMI20_CONTROL, 0x10001f11); + /* Support DVI mode */ + rk628_i2c_write(rk628, HDMI_RX_HDMI_TIMER_CTRL, 0xa78); rk628_i2c_write(rk628, HDMI_RX_HDMI_MODE_RECOVER, 0x00000021); rk628_i2c_write(rk628, HDMI_RX_PDEC_CTRL, 0xbfff8011); rk628_i2c_write(rk628, HDMI_RX_PDEC_ASP_CTRL, 0x00000040); @@ -101,7 +438,14 @@ static void rk628_hdmirx_ctrl_enable(struct rk628 *rk628) rk628_i2c_write(rk628, HDMI_RX_CHLOCK_CONFIG, 0x0030c15c); rk628_i2c_write(rk628, HDMI_RX_HDMI_ERROR_PROTECT, 0x000d0c98); rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL1, 0x00000010); - rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL2, 0x00001738); + /* + * rk628f should set start of horizontal measurement to 3/8 of frame duration + * to pass hdmi 2.0 cts + */ + if (rk628->version == RK628D_VERSION) + rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL2, 0x00001738); + else + rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL2, 0x0000173a); rk628_i2c_write(rk628, HDMI_RX_MD_VCTRL, 0x00000002); rk628_i2c_write(rk628, HDMI_RX_MD_VTH, 0x0000073a); rk628_i2c_write(rk628, HDMI_RX_MD_IL_POL, 0x00000004); @@ -133,11 +477,11 @@ static void rk628_hdmirx_hpd_ctrl(struct rk628 *rk628, bool en) struct rk628_hdmirx *hdmirx = rk628->hdmirx; dev_dbg(rk628->dev, "%s: %sable, hpd invert:%d\n", __func__, - en ? "en" : "dis", hdmirx->hpd_output_inverted); + en ? "en" : "dis", hdmirx->hpd_output_inverted); en_level = hdmirx->hpd_output_inverted ? 0 : 1; set_level = en ? en_level : !en_level; - rk628_i2c_update_bits(rk628, HDMI_RX_HDMI_SETUP_CTRL, - HOT_PLUG_DETECT_MASK, HOT_PLUG_DETECT(set_level)); + + rk628_misc_gpio_direction_output(rk628, GPIO1_B0, set_level); } static void rk628_hdmirx_disable_edid(struct rk628 *rk628) @@ -148,6 +492,7 @@ static void rk628_hdmirx_disable_edid(struct rk628 *rk628) static void rk628_hdmirx_enable_edid(struct rk628 *rk628) { + rk628_i2c_write(rk628, HDMI_RX_SCDC_CONFIG, 0x00000001); rk628_hdmirx_hpd_ctrl(rk628, true); } @@ -168,7 +513,7 @@ static int tx_5v_power_present(struct rk628 *rk628) usleep_range(500, 600); } - ret = (cnt >= 3) ? 1 : 0; + ret = (cnt == 5) ? 1 : 0; dev_dbg(rk628->dev, "%s: %d\n", __func__, ret); return ret; @@ -180,85 +525,63 @@ static int rk628_hdmirx_init_edid(struct rk628 *rk628) struct rk628_hdmirx *hdmirx = rk628->hdmirx; u32 val; u8 csum = 0; - int i, base, j; + int i, base = 0x36; src_mode = rk628_display_get_src_mode(rk628); - for (j = 0, base = 0x36; j < 2; j++) { - csum = 0; - /* clock-frequency */ - edid_init_data[base + 1] = ((src_mode->clock / 10) & 0xff00) >> 8; - edid_init_data[base] = (src_mode->clock / 10) & 0xff; - /* hactive low 8 bits */ - edid_init_data[base + 2] = src_mode->hdisplay & 0xff; - - /* hblanking low 8 bits */ - val = src_mode->htotal - src_mode->hdisplay; - edid_init_data[base + 3] = val & 0xff; - - /* hactive high 4 bits & hblanking low 4 bits */ - edid_init_data[base + 4] = - ((src_mode->hdisplay & 0xf00) >> 4) + ((val & 0xf00) >> 8); - - /* vactive low 8 bits */ - edid_init_data[base + 5] = src_mode->vdisplay & 0xff; - - /* vblanking low 8 bits */ - val = src_mode->vtotal - src_mode->vdisplay; - edid_init_data[base + 6] = val & 0xff; - - /* vactive high 4 bits & vblanking low 4 bits */ - edid_init_data[base + 7] = - ((src_mode->vdisplay & 0xf00) >> 4) + ((val & 0xf00) >> 8); - - /* hsync pulse offset low 8 bits */ - val = src_mode->hsync_start - src_mode->hdisplay; - edid_init_data[base + 8] = val & 0xff; - - /* hsync pulse width low 8 bits */ - val = src_mode->hsync_end - src_mode->hsync_start; - edid_init_data[base + 9] = val & 0xff; - - /* vsync pulse offset low 4 bits & vsync pulse width low 4 bits */ - val = ((src_mode->vsync_start - src_mode->vdisplay) & 0xf) << 4; - edid_init_data[base + 10] = val; - edid_init_data[base + 10] += (src_mode->vsync_end - src_mode->vsync_start) & 0xf; - - /* 6~7bits:hsync pulse offset; - * 4~6bits:hsync pulse width; - * 2~3bits:vsync pulse offset; - * 0~1bits:vsync pulse width - */ - edid_init_data[base + 11] = - ((src_mode->hsync_start - src_mode->hdisplay) & 0x300) >> 2; - edid_init_data[base + 11] += - ((src_mode->hsync_end - src_mode->hsync_start) & 0x700) >> 4; - edid_init_data[base + 11] += - ((src_mode->vsync_start - src_mode->vdisplay) & 0x30) >> 2; - edid_init_data[base + 11] += - ((src_mode->vsync_end - src_mode->vsync_start) & 0x30) >> 4; - - edid_init_data[base + 17] = 0x18; - if (src_mode->flags & DRM_MODE_FLAG_PHSYNC) - edid_init_data[base + 17] |= 0x2; - - if (src_mode->flags & DRM_MODE_FLAG_PVSYNC) - edid_init_data[base + 17] |= 0x4; - - if (hdmirx->src_mode_4K_yuv420 && src_mode->clock == 594000) { - edid_init_data[0x80 + 0x2] = 0x16; - edid_init_data[0x80 + 0x13] = 0xe2; - edid_init_data[0x80 + 0x14] = 0x0E; - edid_init_data[0x80 + 0x15] = 0x61; - base += (0x5d + 0x3); - } else { - base += 0x5d; - } - - for (i = 0; i < 127; i++) - csum += edid_init_data[i + j * 128]; - - edid_init_data[127 + j * 128] = (u8)0 - csum; - } + csum = 0; + /* clock-frequency */ + edid_init_data[base + 1] = ((src_mode->clock / 10) & 0xff00) >> 8; + edid_init_data[base] = (src_mode->clock / 10) & 0xff; + /* hactive low 8 bits */ + edid_init_data[base + 2] = src_mode->hdisplay & 0xff; + /* hblanking low 8 bits */ + val = src_mode->htotal - src_mode->hdisplay; + edid_init_data[base + 3] = val & 0xff; + /* hactive high 4 bits & hblanking low 4 bits */ + edid_init_data[base + 4] = + ((src_mode->hdisplay & 0xf00) >> 4) + ((val & 0xf00) >> 8); + /* vactive low 8 bits */ + edid_init_data[base + 5] = src_mode->vdisplay & 0xff; + /* vblanking low 8 bits */ + val = src_mode->vtotal - src_mode->vdisplay; + edid_init_data[base + 6] = val & 0xff; + /* vactive high 4 bits & vblanking low 4 bits */ + edid_init_data[base + 7] = + ((src_mode->vdisplay & 0xf00) >> 4) + ((val & 0xf00) >> 8); + /* hsync pulse offset low 8 bits */ + val = src_mode->hsync_start - src_mode->hdisplay; + edid_init_data[base + 8] = val & 0xff; + /* hsync pulse width low 8 bits */ + val = src_mode->hsync_end - src_mode->hsync_start; + edid_init_data[base + 9] = val & 0xff; + /* vsync pulse offset low 4 bits & vsync pulse width low 4 bits */ + val = ((src_mode->vsync_start - src_mode->vdisplay) & 0xf) << 4; + edid_init_data[base + 10] = val; + edid_init_data[base + 10] += (src_mode->vsync_end - src_mode->vsync_start) & 0xf; + /* 6~7bits:hsync pulse offset; + * 4~6bits:hsync pulse width; + * 2~3bits:vsync pulse offset; + * 0~1bits:vsync pulse width + */ + edid_init_data[base + 11] = + ((src_mode->hsync_start - src_mode->hdisplay) & 0x300) >> 2; + edid_init_data[base + 11] += + ((src_mode->hsync_end - src_mode->hsync_start) & 0x700) >> 4; + edid_init_data[base + 11] += + ((src_mode->vsync_start - src_mode->vdisplay) & 0x30) >> 2; + edid_init_data[base + 11] += + ((src_mode->vsync_end - src_mode->vsync_start) & 0x30) >> 4; + edid_init_data[base + 17] = 0x18; + if (src_mode->flags & DRM_MODE_FLAG_PHSYNC) + edid_init_data[base + 17] |= 0x2; + if (src_mode->flags & DRM_MODE_FLAG_PVSYNC) + edid_init_data[base + 17] |= 0x4; + /* set edid max tmds clk to 340m, hdmi2.0 only support yuv420 */ + if (hdmirx->src_mode_4K_yuv420) + edid_init_data[0x80 + 34] = 0x44; + for (i = 0; i < 127; i++) + csum += edid_init_data[i]; + edid_init_data[127] = (u8)0 - csum; return 0; } @@ -311,7 +634,7 @@ static int rk628_hdmirx_set_edid(struct rk628 *rk628) return 0; } -static int rk628_hdmirx_phy_power_on(struct rk628 *rk628, int f) +static int rk628d_hdmirx_phy_power_on(struct rk628 *rk628, int f) { int ret; bool rxphy_pwron = false; @@ -347,7 +670,7 @@ static void rk628_hdmirx_get_timing(struct rk628 *rk628) u32 val; u32 modetclk_cnt_hs, modetclk_cnt_vs, hs, vs; u32 hofs_pix, hbp, hfp, vbp, vfp; - u32 tmds_clk, tmdsclk_cnt; + u32 tmds_clk, tmdsclk_cnt, modetclk_hz; u64 tmp_data; u32 interlaced; u32 hfrontporch, hsync, hbackporch, vfrontporch, vsync, vbackporch; @@ -374,17 +697,26 @@ static void rk628_hdmirx_get_timing(struct rk628 *rk628) rk628_i2c_read(rk628, HDMI_RX_MD_VOL, &val); vbp = (val & 0xffff) + 1; + modetclk_hz = rk628_cru_clk_get_rate(rk628, CGU_CLK_CPLL) / 24; rk628_i2c_read(rk628, HDMI_RX_HDMI_CKM_RESULT, &val); tmdsclk_cnt = val & 0xffff; tmp_data = tmdsclk_cnt; - tmp_data = ((tmp_data * MODETCLK_HZ) + MODETCLK_CNT_NUM / 2); + /* rk628d modet clk is always 49.5m, rk628f's freq changes with ref clock */ + if (rk628->version != RK628D_VERSION) + tmp_data = ((tmp_data * modetclk_hz) + MODETCLK_CNT_NUM / 2); + else + tmp_data = ((tmp_data * MODETCLK_HZ) + MODETCLK_CNT_NUM / 2); do_div(tmp_data, MODETCLK_CNT_NUM); tmds_clk = tmp_data; if (!(htotal && vtotal)) { dev_info(rk628->dev, "timing err, htotal:%d, vtotal:%d\n", htotal, vtotal); return; } - fps = (tmds_clk + (htotal * vtotal) / 2) / (htotal * vtotal); + /* rk628f should get exact frame rate frequency to pass hdmi2.0 cts */ + if (rk628->version != RK628D_VERSION) + fps = tmds_clk / (htotal * vtotal); + else + fps = (tmds_clk + (htotal * vtotal) / 2) / (htotal * vtotal); rk628_i2c_read(rk628, HDMI_RX_MD_HT0, &val); modetclk_cnt_hs = val & 0xffff; @@ -427,7 +759,11 @@ static void rk628_hdmirx_get_timing(struct rk628 *rk628) vfrontporch = vfp; vsync = vs; vbackporch = vbp; - pixelclock = htotal * vtotal * fps; + /* rk628f should get exact pixel clk frequency to pass hdmi2.0 cts */ + if (rk628->version != RK628D_VERSION) + pixelclock = tmds_clk; + else + pixelclock = htotal * vtotal * fps; if (interlaced == 1) { vsync = vsync + 1; @@ -452,15 +788,58 @@ static void rk628_hdmirx_get_timing(struct rk628 *rk628) hfrontporch, hsync, hbackporch, vfrontporch, vsync, vbackporch, interlaced); } +static void rk628f_hdmirx_phy_power_on(struct rk628 *rk628) +{ + struct rk628_hdmirx *hdmirx = rk628->hdmirx; + + /* power down phy */ + rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x17); + usleep_range(20, 30); + rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x15); + /* init phy i2c */ + rk628_i2c_write(rk628, HDMI_RX_SNPS_PHYG3_CTRL, 0); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_SS_CNTS, 0x018c01d2); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_FS_HCNT, 0x003c0081); + rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_MODE, 1); + rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x11); + /* enable rx phy */ + rk628_hdmirx_phy_enable(rk628, hdmirx->is_hdmi2); + rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x14); +} + +static void rk628_hdmirx_phy_prepclk_cfg(struct rk628 *rk628) +{ + u32 format; + bool is_clrdpt_8bit = false; + + rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &format); + format = (format & VIDEO_FORMAT) >> 5; + + /* yuv420 should set phy color depth 8bit */ + if (format == 3) + is_clrdpt_8bit = true; + + rk628_i2c_read(rk628, HDMI_RX_PDEC_GCP_AVMUTE, &format); + format = (format & PKTDEC_GCP_CD_MASK) >> 4; + + /* 10bit color depth should set phy color depth 8bit */ + if (format == 5) + is_clrdpt_8bit = true; + + rk628_hdmirx_phy_set_clrdpt(rk628, is_clrdpt_8bit); +} + static int rk628_hdmirx_phy_setup(struct rk628 *rk628) { - u32 i, cnt, val; - u32 width, height, frame_width, frame_height, status; + u32 i, cnt, val, status, vs; + u32 width, height, frame_width, frame_height, tmdsclk_cnt, modetclk_cnt_hs, modetclk_cnt_vs; struct rk628_display_mode *src_mode; struct rk628_hdmirx *hdmirx = rk628->hdmirx; - int f; - struct rk628_display_mode *dst_mode; + u32 f; + bool signal_input, timing_detected; + src_mode = rk628_display_get_src_mode(rk628); + f = src_mode->clock; /* Bit31 is used to distinguish HDMI cable mode and direct connection * mode in the rk628_combrxphy driver. * Bit31: 0 -direct connection mode; @@ -469,22 +848,49 @@ static int rk628_hdmirx_phy_setup(struct rk628 *rk628) * in the rk628_combrxphy driver, and the cable mode supports up to * 297M, so 297M is passed uniformly here. */ - f = (297000 | BIT(31)); - dst_mode = rk628_display_get_dst_mode(rk628); + if (rk628->plugin_det_gpio) + f |= BIT(31); /* * force 594m mode to yuv420 format. * bit30 is used to indicate whether it is yuv420 format. */ - if (hdmirx->src_mode_4K_yuv420 && dst_mode->clock == 594000) + if (hdmirx->src_mode_4K_yuv420) { + f /= 2; f |= BIT(30); + } + + if (!rk628->plugin_det_gpio) { + u32 tmds_rate = src_mode->clock; + + if (hdmirx->src_mode_4K_yuv420) + tmds_rate /= 2; + if (hdmirx->src_depth_10bit) + tmds_rate = tmds_rate * 10 / 8; + if (tmds_rate >= 340000) + hdmirx->is_hdmi2 = true; + else + hdmirx->is_hdmi2 = false; + } + + /* enable scramble in hdmi2.0 mode */ + if (hdmirx->is_hdmi2 && !rk628->plugin_det_gpio) + rk628_i2c_write(rk628, HDMI_RX_HDMI20_CONTROL, 0x10001f13); for (i = 0; i < RXPHY_CFG_MAX_TIMES; i++) { - rk628_hdmirx_phy_power_on(rk628, f); + if (rk628->version == RK628D_VERSION) + rk628d_hdmirx_phy_power_on(rk628, f); + else if (rk628->version == RK628F_VERSION) + rk628f_hdmirx_phy_power_on(rk628); + + signal_input = false; + cnt = 0; do { - cnt++; - udelay(2000); + if (signal_input || rk628->plugin_det_gpio || + rk628->version == RK628D_VERSION) + cnt++; + msleep(100); rk628_i2c_read(rk628, HDMI_RX_MD_HACT_PX, &val); width = val & 0xffff; rk628_i2c_read(rk628, HDMI_RX_MD_HT1, &val); @@ -495,26 +901,64 @@ static int rk628_hdmirx_phy_setup(struct rk628 *rk628) rk628_i2c_read(rk628, HDMI_RX_MD_VTL, &val); frame_height = val & 0xffff; + rk628_i2c_read(rk628, HDMI_RX_HDMI_CKM_RESULT, &val); + tmdsclk_cnt = val & 0xffff; + rk628_i2c_read(rk628, HDMI_RX_MD_HT0, &val); + modetclk_cnt_hs = val & 0xffff; + rk628_i2c_read(rk628, HDMI_RX_MD_VSC, &val); + modetclk_cnt_vs = val & 0xffff; + + if (frame_width) { + vs = (tmdsclk_cnt * modetclk_cnt_vs + MODETCLK_CNT_NUM / 2) / + MODETCLK_CNT_NUM; + vs = (vs + frame_width / 2) / frame_width; + } else { + vs = 0; + } + + if (width && height && frame_width && frame_height && tmdsclk_cnt && + modetclk_cnt_hs && modetclk_cnt_vs && vs) + timing_detected = true; + else + timing_detected = false; + rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS1, &val); status = val; dev_info(rk628->dev, - "%s read wxh:%dx%d, total:%dx%d, SCDC_REGS1:%#x, cnt:%d\n", - __func__, width, height, frame_width, + "tmdsclk_cnt:%d, modetclk_cnt_hs:%d, modetclk_cnt_vs:%d,vs:%d\n", + tmdsclk_cnt, modetclk_cnt_hs, modetclk_cnt_vs, vs); + dev_info(rk628->dev, + "read wxh:%dx%d, total:%dx%d, SCDC_REGS1:%#x, cnt:%d\n", + width, height, frame_width, frame_height, status, cnt); + /* no signal input, wait */ + if (status & 0xf00) + signal_input = true; + if (cnt >= 15) break; - } while ((status & 0xfff) != 0xf00); + } while (((status & 0xfff) != 0xf00) || !timing_detected); - if ((status & 0xfff) != 0xf00) { - dev_info(rk628->dev, "%s hdmi rxphy lock failed, retry:%d\n", - __func__, i); + if (((status & 0xfff) != 0xf00) || (((status >> 16) > 0xc000) && + rk628->version != RK628D_VERSION)) { + dev_info(rk628->dev, "%s hdmi rxphy lock failed, retry:%d, status:0x%x\n", + __func__, i, status); + if (((status >> 16) > 0xc000)) + dev_info(rk628->dev, "((status >> 16) > 0xc000)\n"); continue; } else { rk628_hdmirx_get_timing(rk628); - src_mode = rk628_display_get_src_mode(rk628); + if (hdmirx->mode.flags & DRM_MODE_FLAG_INTERLACE) { + dev_info(rk628->dev, "interlace mode is unsupported\n"); + continue; + } + + if (hdmirx->mode.clock == 0) + return -EINVAL; + src_mode->clock = hdmirx->mode.clock; src_mode->hdisplay = hdmirx->mode.hdisplay; src_mode->hsync_start = hdmirx->mode.hstart; @@ -526,10 +970,6 @@ static int rk628_hdmirx_phy_setup(struct rk628 *rk628) src_mode->vsync_end = hdmirx->mode.vend; src_mode->vtotal = hdmirx->mode.vtotal; src_mode->flags = hdmirx->mode.flags; - if (hdmirx->src_mode_4K_yuv420 && dst_mode->clock == 594000) { - rk628_mode_copy(src_mode, dst_mode); - src_mode->flags = DRM_MODE_FLAG_PHSYNC|DRM_MODE_FLAG_PVSYNC; - } break; } @@ -641,42 +1081,61 @@ static int rk628_hdmirx_init(struct rk628 *rk628) if (!hdmirx) return -ENOMEM; rk628->hdmirx = hdmirx; + hdmirx->rk628 = rk628; - hdmirx->hpd_output_inverted = of_property_read_bool(dev->of_node, - "hpd-output-inverted"); + /* + * rk628f hdmirx phy can only access via hdmirx controller inner i2c. + * rk628d phy regs can access via rk628 i2c, there is no need to create + * debugfs. + */ + if (rk628->version != RK628D_VERSION && !IS_ERR(rk628->debug_dir)) + debugfs_create_file("hdmirx_phy", 0600, rk628->debug_dir, + rk628, &rk628_hdmirx_phy_fops); - hdmirx->src_mode_4K_yuv420 = of_property_read_bool(dev->of_node, - "src-mode-4k-yuv420"); + hdmirx->hpd_output_inverted = of_property_read_bool(dev->of_node, "hpd-output-inverted"); + + hdmirx->src_mode_4K_yuv420 = of_property_read_bool(dev->of_node, "src-mode-4k-yuv420"); + + hdmirx->src_depth_10bit = of_property_read_bool(dev->of_node, "src-depth-10bit"); /* HDMIRX IOMUX */ rk628_i2c_write(rk628, GRF_GPIO1AB_SEL_CON, HIWORD_UPDATE(0x7, 10, 8)); //i2s pinctrl rk628_i2c_write(rk628, GRF_GPIO0AB_SEL_CON, 0x155c155c); + /* enable */ + rk628_i2c_write(rk628, GRF_GPIO1AB_SEL_CON, HIWORD_UPDATE(0, 0, 0)); + rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, HIWORD_UPDATE(1, 14, 14)); + /* if GVI and HDMITX OUT, HDMIRX missing signal */ rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, - SW_OUTPUT_MODE_MASK, SW_OUTPUT_MODE(OUTPUT_MODE_RGB)); + SW_OUTPUT_RGB_MODE_MASK, + SW_OUTPUT_RGB_MODE(OUTPUT_MODE_RGB >> 3)); + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_INPUT_MODE_MASK, SW_INPUT_MODE(INPUT_MODE_HDMI)); rk628_hdmirx_set_edid(rk628); /* clear avi rcv interrupt */ rk628_i2c_write(rk628, HDMI_RX_PDEC_ICLR, AVI_RCV_ISTS); + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_I2S_DATA_OEN_MASK, SW_I2S_DATA_OEN(0)); dev_info(rk628->dev, "hdmirx driver version: %s\n", DRIVER_VERSION); - + INIT_DELAYED_WORK(&hdmirx->delayed_work_audio, rk628_hdmirx_delayed_work_audio); return 0; } void rk628_hdmirx_enable_interrupts(struct rk628 *rk628, bool en) { - u32 pdec_ien, md_ien; + u32 md_ien; u32 md_mask = 0; - md_mask = VACT_LIN_ENSET | HACT_PIX_ENSET | HS_CLK_ENSET; + md_mask = VACT_LIN_ENSET | HACT_PIX_ENSET | HS_CLK_ENSET | DE_ACTIVITY_ENSET | + VS_ACT_ENSET | HS_ACT_ENSET; + dev_dbg(rk628->dev, "%s: %sable\n", __func__, en ? "en" : "dis"); - /* clr irq */ - rk628_i2c_write(rk628, HDMI_RX_MD_ICLR, md_mask); if (en) { + /* clr irq */ + rk628_i2c_write(rk628, HDMI_RX_MD_ICLR, md_mask); rk628_i2c_write(rk628, HDMI_RX_MD_IEN_SET, md_mask); } else { rk628_i2c_write(rk628, HDMI_RX_MD_IEN_CLR, md_mask); @@ -684,14 +1143,15 @@ void rk628_hdmirx_enable_interrupts(struct rk628 *rk628, bool en) } usleep_range(5000, 5000); rk628_i2c_read(rk628, HDMI_RX_MD_IEN, &md_ien); - rk628_i2c_read(rk628, HDMI_RX_PDEC_IEN, &pdec_ien); - dev_dbg(rk628->dev, "%s MD_IEN:%#x, PDEC_IEN:%#x\n", __func__, md_ien, pdec_ien); + dev_dbg(rk628->dev, "%s MD_IEN:%#x\n", __func__, md_ien); } int rk628_hdmirx_enable(struct rk628 *rk628) { int ret; + u32 val; struct rk628_hdmirx *hdmirx; + struct rk628_display_mode *src_mode; if (!rk628->hdmirx) { ret = rk628_hdmirx_init(rk628); @@ -700,20 +1160,77 @@ int rk628_hdmirx_enable(struct rk628 *rk628) } hdmirx = rk628->hdmirx; + if (tx_5v_power_present(rk628)) { + int i; + hdmirx->plugin = true; + hdmirx->is_hdmi2 = false; rk628_hdmirx_enable_edid(rk628); + if (rk628->plugin_det_gpio) { + for (i = 0; i < 20; i++) { + msleep(50); + rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS0, &val); + dev_info(rk628->dev, "HDMI_RX_SCDC_REGS0:0x%x\n", val); + if (val & SCDC_TMDSBITCLKRATIO) { + hdmirx->is_hdmi2 = true; + break; + } + } + } + + /* rst for hdmirx */ + rk628_hdmirx_reset_control_assert(rk628); + usleep_range(20, 40); + rk628_hdmirx_reset_control_deassert(rk628); + usleep_range(20, 40); + rk628_hdmirx_ctrl_enable(rk628); - rk628_hdmirx_phy_setup(rk628); + ret = rk628_hdmirx_phy_setup(rk628); + if (ret < 0) + return HDMIRX_PLUGIN | HDMIRX_NOSIGNAL; + rk628_hdmirx_audio_setup(rk628); + rk628_hdmirx_get_input_format(rk628); + + src_mode = rk628_display_get_src_mode(rk628); + if (hdmirx->input_format == BUS_FMT_YUV420) { + /* yuv420 horizontal timing is 1/2 */ + if (hdmirx->src_depth_10bit) { + src_mode->clock = src_mode->clock * 8 * 2 / 10; + src_mode->hdisplay = src_mode->hdisplay * 8 * 2 / 10; + src_mode->hsync_start = src_mode->hsync_start * 8 * 2 / 10; + src_mode->hsync_end = src_mode->hsync_end * 8 * 2 / 10; + src_mode->htotal = src_mode->htotal * 8 * 2 / 10; + } else { + src_mode->clock *= 2; + src_mode->hdisplay *= 2; + src_mode->hsync_start *= 2; + src_mode->hsync_end *= 2; + src_mode->htotal *= 2; + } + } + + if (rk628->version != RK628D_VERSION) + rk628_hdmirx_phy_prepclk_cfg(rk628); rk628_set_input_bus_format(rk628, hdmirx->input_format); dev_info(rk628->dev, "hdmirx plug in\n"); dev_info(rk628->dev, "input: %d, output: %d\n", hdmirx->input_format, rk628_get_output_bus_format(rk628)); - if (!rk628_check_signal(rk628)) + if (!rk628_check_signal(rk628) || !hdmirx->phy_lock) return HDMIRX_PLUGIN | HDMIRX_NOSIGNAL; + dev_info(rk628->dev, "hdmirx success\n"); + if (rk628->version != RK628D_VERSION) { + /* Try to keep pll frequency close to 1188m */ + val = DIV_ROUND_CLOSEST_ULL(1188000000, (src_mode->clock * 1000)); + val *= src_mode->clock * 1000; + /* set pll rate according hdmirx tmds clk */ + rk628_cru_clk_set_rate(rk628, CGU_CLK_CPLL, val); + msleep(50); + } rk628_hdmirx_video_unmute(rk628, 1); + return HDMIRX_PLUGIN; } @@ -743,11 +1260,14 @@ void rk628_hdmirx_disable(struct rk628 *rk628) SW_I2S_DATA_OEN(1)); dev_info(rk628->dev, "hdmirx plug out\n"); } + + cancel_delayed_work_sync(&hdmirx->delayed_work_audio); } int rk628_hdmirx_detect(struct rk628 *rk628) { int ret = 0; + u32 val; struct rk628_hdmirx *hdmirx; if (!rk628->hdmirx) { @@ -763,6 +1283,13 @@ int rk628_hdmirx_detect(struct rk628 *rk628) ret |= HDMIRX_CHANGED; if (rk628_hdmirx_status_change(rk628)) ret |= HDMIRX_CHANGED; + + if (hdmirx->phy_lock) { + rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS1, &val); + if ((val & 0xfff) != 0xf00) + hdmirx->phy_lock = false; + } + if (!hdmirx->phy_lock) ret |= HDMIRX_NOLOCK; hdmirx->plugin = true; diff --git a/drivers/misc/rk628/rk628_hdmirx.h b/drivers/misc/rk628/rk628_hdmirx.h index cb7407a0e623..ae159a7d23c4 100644 --- a/drivers/misc/rk628/rk628_hdmirx.h +++ b/drivers/misc/rk628/rk628_hdmirx.h @@ -427,6 +427,7 @@ #define MILISECTIMERLIMIT(x) UPDATE(x, 15, 0) #define HDMI_RX_HDCP22_CONTROL HDMIRX_REG(0x081c) #define HDMI_RX_SCDC_REGS0 HDMIRX_REG(0x0820) +#define SCDC_TMDSBITCLKRATIO BIT(17) #define HDMI_RX_SCDC_REGS1 HDMIRX_REG(0x0824) #define HDMI_RX_SCDC_REGS2 HDMIRX_REG(0x0828) #define HDMI_RX_SCDC_REGS3 HDMIRX_REG(0x082c) @@ -482,6 +483,11 @@ #define HDMI_RX_AUD_CEC_ISET HDMIRX_REG(0x0fa4) #define HDMI_RX_AUD_FIFO_IEN_CLR HDMIRX_REG(0x0fa8) #define HDMI_RX_AUD_FIFO_IEN_SET HDMIRX_REG(0x0fac) +#define AFIF_OVERFL_ISTS BIT(4) +#define AFIF_UNDERFL_ISTS BIT(3) +#define AFIF_THS_PASS_ISTS BIT(2) +#define AFIF_TH_MAX_ISTS BIT(1) +#define AFIF_TH_MIN_ISTS BIT(0) #define HDMI_RX_AUD_FIFO_ISTS HDMIRX_REG(0x0fb0) #define HDMI_RX_AUD_FIFO_IEN HDMIRX_REG(0x0fb4) #define HDMI_RX_AUD_FIFO_ICLR HDMIRX_REG(0x0fb8) diff --git a/drivers/misc/rk628/rk628_hdmitx.c b/drivers/misc/rk628/rk628_hdmitx.c index eaa1c2ea0cec..8a9a6cf1c139 100644 --- a/drivers/misc/rk628/rk628_hdmitx.c +++ b/drivers/misc/rk628/rk628_hdmitx.c @@ -462,6 +462,7 @@ static int rk628_hdmi_config_video_timing(struct rk628_hdmi *hdmi, struct drm_display_mode *mode) { int value; + u32 hdmitx_sync_pol = 0; /* Set detail external video timing polarity and interlace mode */ value = EXTERANL_VIDEO(1); @@ -472,6 +473,12 @@ static int rk628_hdmi_config_video_timing(struct rk628_hdmi *hdmi, value |= mode->flags & DRM_MODE_FLAG_INTERLACE ? INETLACE(1) : INETLACE(0); hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value); + hdmitx_sync_pol |= mode->flags & DRM_MODE_FLAG_PVSYNC ? + SW_HDMITX_VSYNC_POL : 0; + hdmitx_sync_pol |= mode->flags & DRM_MODE_FLAG_PHSYNC ? + SW_HDMITX_HSYNC_POL : 0; + rk628_i2c_update_bits(hdmi->rk628, GRF_POST_PROC_CON, + SW_HDMITX_VSYNC_POL | SW_HDMITX_HSYNC_POL, hdmitx_sync_pol); /* Set detail external video timing */ value = mode->htotal; @@ -604,7 +611,12 @@ static int rk628_hdmi_connector_get_modes(struct drm_connector *connector) ret = rockchip_drm_add_modes_noedid(connector); +#if KERNEL_VERSION(5, 18, 0) <= LINUX_VERSION_CODE + info->edid_hdmi_ycbcr444_dc_modes = 0; + info->edid_hdmi_rgb444_dc_modes = 0; +#else info->edid_hdmi_dc_modes = 0; +#endif info->hdmi.y420_dc_modes = 0; info->color_formats = 0; @@ -709,13 +721,6 @@ static void rk628_hdmi_bridge_enable(struct drm_bridge *bridge) rk628_hdmi_set_pwr_mode(hdmi, NORMAL); } -static void rk628_hdmi_bridge_disable(struct drm_bridge *bridge) -{ - struct rk628_hdmi *hdmi = bridge_to_hdmi(bridge); - - rk628_hdmi_set_pwr_mode(hdmi, LOWER_PWR); -} - static int rk628_hdmi_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { @@ -760,7 +765,6 @@ static const struct drm_bridge_funcs rk628_hdmi_bridge_funcs = { .mode_set = rk628_hdmi_bridge_mode_set, .mode_fixup = rk628_hdmi_bridge_mode_fixup, .enable = rk628_hdmi_bridge_enable, - .disable = rk628_hdmi_bridge_disable, }; static int @@ -1091,13 +1095,127 @@ static struct i2c_adapter *rk628_hdmi_i2c_adapter(struct rk628_hdmi *hdmi) return adap; } +static int rk628_hdmitx_color_bar_show(struct seq_file *s, void *data) +{ + seq_puts(s, " Enable normal color bar:\n"); + seq_puts(s, " example: echo 1 > /sys/kernel/debug/rk628/2-0050/hdmitx_color_bar\n"); + seq_puts(s, " Enable special color bar:\n"); + seq_puts(s, " example: echo 2 > /sys/kernel/debug/rk628/2-0050/hdmitx_color_bar\n"); + seq_puts(s, " Enable black color bar:\n"); + seq_puts(s, " example: echo 3 > /sys/kernel/debug/rk628/2-0050/hdmitx_color_bar\n"); + seq_puts(s, " Disable color bar:\n"); + seq_puts(s, " example: echo 0 > /sys/kernel/debug/rk628/2-0050/hdmitx_color_bar\n"); + + return 0; +} + +static int rk628_hdmitx_color_bar_open(struct inode *inode, struct file *file) +{ + return single_open(file, rk628_hdmitx_color_bar_show, inode->i_private); +} + +static ssize_t rk628_hdmitx_color_bar_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct rk628 *rk628 = ((struct seq_file *)file->private_data)->private; + u8 mode; + + if (kstrtou8_from_user(ubuf, len, 0, &mode)) + return -EFAULT; + + switch (mode) { + case 0: + rk628_i2c_update_bits(rk628, HDMI_COLOR_BAR, DISABLE_COLORBAR_BIST_MASK, + DISABLE_COLORBAR_BIST(1)); + break; + case 1: + rk628_i2c_update_bits(rk628, HDMI_COLOR_BAR, DISABLE_COLORBAR_BIST_MASK, + DISABLE_COLORBAR_BIST(0)); + rk628_i2c_update_bits(rk628, HDMI_COLOR_BAR, VIDEO_BIST_MODE_MASK, + VIDEO_BIST_MODE(0)); + break; + case 2: + rk628_i2c_update_bits(rk628, HDMI_COLOR_BAR, DISABLE_COLORBAR_BIST_MASK, + DISABLE_COLORBAR_BIST(0)); + rk628_i2c_update_bits(rk628, HDMI_COLOR_BAR, VIDEO_BIST_MODE_MASK, + VIDEO_BIST_MODE(1)); + break; + case 3: + default: + rk628_i2c_update_bits(rk628, HDMI_COLOR_BAR, DISABLE_COLORBAR_BIST_MASK, + DISABLE_COLORBAR_BIST(0)); + rk628_i2c_update_bits(rk628, HDMI_COLOR_BAR, VIDEO_BIST_MODE_MASK, + VIDEO_BIST_MODE(2)); + } + + return len; +} + +static const struct file_operations rk628_hdmitx_color_bar_fops = { + .owner = THIS_MODULE, + .open = rk628_hdmitx_color_bar_open, + .read = seq_read, + .write = rk628_hdmitx_color_bar_write, + .llseek = seq_lseek, + .release = single_release, +}; + +void rk628_hdmitx_create_debugfs_file(struct rk628 *rk628) +{ + if (rk628_output_is_hdmi(rk628)) + debugfs_create_file("hdmitx_color_bar", 0600, rk628->debug_dir, + rk628, &rk628_hdmitx_color_bar_fops); +} + +void rk628_hdmitx_disable(struct rk628 *rk628) +{ + rk628_i2c_update_bits(rk628, HDMI_SYS_CTRL, POWER_MASK, PWR_OFF(1)); + rk628_i2c_write(rk628, HDMI_PHY_DRIVER, 0x00); + rk628_i2c_write(rk628, HDMI_PHY_PRE_EMPHASIS, 0x00); + rk628_i2c_write(rk628, HDMI_PHY_CHG_PWR, 0x00); + rk628_i2c_write(rk628, HDMI_PHY_SYS_CTL, 0x15); +} + int rk628_hdmitx_enable(struct rk628 *rk628) { struct device *dev = rk628->dev; struct rk628_hdmi *hdmi; + u32 mask = SW_OUTPUT_MODE_MASK; + u32 val = SW_OUTPUT_MODE(OUTPUT_MODE_HDMI); int irq; int ret; + /* select int io function */ + rk628_i2c_write(rk628, GRF_GPIO0AB_SEL_CON, 0x70007000); + rk628_i2c_write(rk628, GRF_GPIO0AB_SEL_CON, 0x055c055c); + + /* hdmitx vclk pllref select Pin_vclk */ + rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, + SW_HDMITX_VCLK_PLLREF_SEL_MASK, + SW_HDMITX_VCLK_PLLREF_SEL(1)); + + if (rk628->version == RK628F_VERSION) { + mask = SW_HDMITX_EN_MASK; + val = SW_HDMITX_EN(1); + } + + /* set output mode to HDMI */ + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, mask, val); + /* hdmitx int en */ + rk628_i2c_write(rk628, GRF_INTR0_EN, 0x00040004); + + + if (rk628->hdmitx) { + hdmi = rk628->hdmitx; + rk628_hdmi_reset(hdmi); + rk628_hdmi_i2c_init(hdmi); + /* Unmute hotplug interrupt */ + hdmi_modb(hdmi, HDMI_STATUS, MASK_INT_HOTPLUG_MASK, + MASK_INT_HOTPLUG(1)); + + return 0; + } + if (!of_device_is_available(dev->of_node)) return -ENODEV; @@ -1108,24 +1226,13 @@ int rk628_hdmitx_enable(struct rk628 *rk628) hdmi->dev = dev; hdmi->rk628 = rk628; + rk628->hdmitx = hdmi; irq = rk628->client->irq; if (irq < 0) return irq; dev_set_drvdata(dev, hdmi); - /* selete int io function */ - rk628_i2c_write(rk628, GRF_GPIO0AB_SEL_CON, 0x70007000); - rk628_i2c_write(rk628, GRF_GPIO0AB_SEL_CON, 0x055c055c); - - /* hdmitx vclk pllref select Pin_vclk */ - rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, - SW_HDMITX_VCLK_PLLREF_SEL_MASK, - SW_HDMITX_VCLK_PLLREF_SEL(1)); - /* set output mode to HDMI */ - rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_OUTPUT_MODE_MASK, - SW_OUTPUT_MODE(OUTPUT_MODE_HDMI)); - rk628_hdmi_reset(hdmi); hdmi->ddc = rk628_hdmi_i2c_adapter(hdmi); @@ -1142,9 +1249,6 @@ int rk628_hdmitx_enable(struct rk628 *rk628) * and reconfigure the DDC clock. */ hdmi->tmds_rate = 24000 * 1000; - - /* hdmitx int en */ - rk628_i2c_write(rk628, GRF_INTR0_EN, 0x00040004); rk628_hdmi_i2c_init(hdmi); rk628_hdmi_audio_codec_init(hdmi, dev); @@ -1190,6 +1294,8 @@ int rk628_hdmitx_enable(struct rk628 *rk628) return 0; fail: + devm_kfree(dev, hdmi); + rk628->hdmitx = NULL; return ret; } diff --git a/drivers/misc/rk628/rk628_hdmitx.h b/drivers/misc/rk628/rk628_hdmitx.h index 9a1b3b8fa9fa..ff80f0df0e62 100644 --- a/drivers/misc/rk628/rk628_hdmitx.h +++ b/drivers/misc/rk628/rk628_hdmitx.h @@ -342,9 +342,30 @@ enum { #define HDMI_CEC_BUSFREETIME_L HDMITX_REG(0xdc) #define HDMI_CEC_BUSFREETIME_H HDMITX_REG(0xdd) #define HDMI_CEC_LOGICADDR HDMITX_REG(0xde) - +#define HDMI_COLOR_BAR HDMITX_REG(0xc9) +#define VIDEO_BIST_MODE_MASK GENMASK(7, 6) +#define VIDEO_BIST_MODE(x) UPDATE(x, 7, 6) +#define DISABLE_COLORBAR_BIST_MASK BIT(4) +#define DISABLE_COLORBAR_BIST(x) UPDATE(x, 4, 4) #define HDMI_MAX_REG HDMITX_REG(0xed) +#ifdef CONFIG_RK628_MISC_HDMITX +void rk628_hdmitx_disable(struct rk628 *rk628); int rk628_hdmitx_enable(struct rk628 *rk628); +void rk628_hdmitx_create_debugfs_file(struct rk628 *rk628); +#else +static inline void rk628_hdmitx_disable(struct rk628 *rk628) +{ +} + +static inline int rk628_hdmitx_enable(struct rk628 *rk628) +{ + return 0; +} + +static inline void rk628_hdmitx_create_debugfs_file(struct rk628 *rk628) +{ +} +#endif #endif diff --git a/drivers/misc/rk628/rk628_lvds.c b/drivers/misc/rk628/rk628_lvds.c index b0587b4daea8..eba187fd9df0 100644 --- a/drivers/misc/rk628/rk628_lvds.c +++ b/drivers/misc/rk628/rk628_lvds.c @@ -5,8 +5,9 @@ * Author: Guochun Huang */ +#include #include "rk628.h" - +#include "rk628_lvds.h" #include "rk628_combtxphy.h" #include "rk628_config.h" #include "panel.h" @@ -24,14 +25,13 @@ static inline void lvds_update_bits(struct rk628 *rk628, u32 reg, int rk628_lvds_parse(struct rk628 *rk628, struct device_node *lvds_np) { - const char *string; + const char *string = NULL; int ret; + u32 bus_format; if (!of_device_is_available(lvds_np)) return -EINVAL; - rk628->output_mode = OUTPUT_MODE_LVDS; - if (!of_property_read_string(lvds_np, "bus-format", &string)) { if (!strcmp(string, "jeida_24")) rk628->lvds.format = LVDS_FORMAT_JEIDA_24BIT; @@ -42,6 +42,24 @@ int rk628_lvds_parse(struct rk628 *rk628, struct device_node *lvds_np) else rk628->lvds.format = LVDS_FORMAT_VESA_24BIT; } + /* fallback to u32 type */ + if (!string || strlen(string) == 0) { + if (!of_property_read_u32(lvds_np, "bus-format", &bus_format)) { + switch (bus_format) { + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: + rk628->lvds.format = LVDS_FORMAT_JEIDA_24BIT; + break; + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: + rk628->lvds.format = LVDS_FORMAT_JEIDA_18BIT; + break; + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: + rk628->lvds.format = LVDS_FORMAT_VESA_24BIT; + break; + default: + rk628->lvds.format = LVDS_FORMAT_VESA_18BIT; + } + } + } if (!of_property_read_string(lvds_np, "link-type", &string)) { if (!strcmp(string, "dual_link_odd_even_pixels")) @@ -68,10 +86,17 @@ void rk628_lvds_enable(struct rk628 *rk628) enum lvds_link_type link_type = rk628->lvds.link_type; enum lvds_format format = rk628->lvds.format; const struct rk628_display_mode *mode = &rk628->dst_mode; - u32 val, bus_width; + u32 val, mask, bus_width; - lvds_update_bits(rk628, GRF_SYSTEM_CON0, SW_OUTPUT_MODE_MASK, - SW_OUTPUT_MODE(OUTPUT_MODE_LVDS)); + mask = SW_OUTPUT_MODE_MASK; + val = SW_OUTPUT_MODE(OUTPUT_MODE_LVDS); + + if (rk628->version == RK628F_VERSION) { + mask = SW_OUTPUT_COMBTX_MODE_MASK; + val = SW_OUTPUT_COMBTX_MODE(OUTPUT_MODE_LVDS); + } + + lvds_update_bits(rk628, GRF_SYSTEM_CON0, mask, val); switch (link_type) { case LVDS_DUAL_LINK_ODD_EVEN_PIXELS: diff --git a/drivers/misc/rk628/rk628_post_process.c b/drivers/misc/rk628/rk628_post_process.c index d5ffa3d69fbd..f6479ace3b92 100644 --- a/drivers/misc/rk628/rk628_post_process.c +++ b/drivers/misc/rk628/rk628_post_process.c @@ -4,10 +4,1274 @@ * * Author: Wyon Bi */ + +#include #include "rk628.h" #include "rk628_config.h" #include "rk628_cru.h" +#define PQ_CSC_HUE_TABLE_NUM 256 +#define PQ_CSC_MODE_COEF_COMMENT_LEN 32 +#define PQ_CSC_SIMPLE_MAT_PARAM_FIX_BIT_WIDTH 10 +#define PQ_CSC_SIMPLE_MAT_PARAM_FIX_NUM (1 << PQ_CSC_SIMPLE_MAT_PARAM_FIX_BIT_WIDTH) + +#define PQ_CALC_ENHANCE_BIT 6 +/* csc convert coef fixed-point num bit width */ +#define PQ_CSC_PARAM_FIX_BIT_WIDTH 10 +/* csc convert coef half fixed-point num bit width */ +#define PQ_CSC_PARAM_HALF_FIX_BIT_WIDTH (PQ_CSC_PARAM_FIX_BIT_WIDTH - 1) +/* csc convert coef fixed-point num */ +#define PQ_CSC_PARAM_FIX_NUM (1 << PQ_CSC_PARAM_FIX_BIT_WIDTH) +#define PQ_CSC_PARAM_HALF_FIX_NUM (1 << PQ_CSC_PARAM_HALF_FIX_BIT_WIDTH) +/* csc input param bit width */ +#define PQ_CSC_IN_PARAM_NORM_BIT_WIDTH 9 +/* csc input param normalization coef */ +#define PQ_CSC_IN_PARAM_NORM_COEF (1 << PQ_CSC_IN_PARAM_NORM_BIT_WIDTH) + +/* csc hue table range [0,255] */ +#define PQ_CSC_HUE_TABLE_DIV_COEF 2 +/* csc brightness offset */ +#define PQ_CSC_BRIGHTNESS_OFFSET 256 + +/* dc coef base bit width */ +#define PQ_CSC_DC_COEF_BASE_BIT_WIDTH 10 +/* input dc coef offset for 10bit data */ +#define PQ_CSC_DC_IN_OFFSET 64 +/* input and output dc coef offset for 10bit data u,v */ +#define PQ_CSC_DC_IN_OUT_DEFAULT 512 +/* r,g,b color temp div coef, range [-128,128] for 10bit data */ +#define PQ_CSC_TEMP_OFFSET_DIV_COEF 2 + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define CLIP(x, min_v, max_v) MIN(MAX(x, min_v), max_v) + +#define V4L2_COLORSPACE_BT709F 0xfe +#define V4L2_COLORSPACE_BT2020F 0xff + +enum vop_csc_bit_depth { + CSC_10BIT_DEPTH, + CSC_13BIT_DEPTH, +}; + +enum rk_pq_csc_mode { + RK_PQ_CSC_YUV2RGB_601 = 0, /* YCbCr_601 LIMIT-> RGB FULL */ + RK_PQ_CSC_YUV2RGB_709, /* YCbCr_709 LIMIT-> RGB FULL */ + RK_PQ_CSC_RGB2YUV_601, /* RGB FULL->YCbCr_601 LIMIT */ + RK_PQ_CSC_RGB2YUV_709, /* RGB FULL->YCbCr_709 LIMIT */ + RK_PQ_CSC_YUV2YUV_709_601, /* YCbCr_709 LIMIT->YCbCr_601 LIMIT */ + RK_PQ_CSC_YUV2YUV_601_709, /* YCbCr_601 LIMIT->YCbCr_709 LIMIT */ + RK_PQ_CSC_YUV2YUV, /* YCbCr LIMIT->YCbCr LIMIT */ + RK_PQ_CSC_YUV2RGB_601_FULL, /* YCbCr_601 FULL-> RGB FULL */ + RK_PQ_CSC_YUV2RGB_709_FULL, /* YCbCr_709 FULL-> RGB FULL */ + RK_PQ_CSC_RGB2YUV_601_FULL, /* RGB FULL->YCbCr_601 FULL */ + RK_PQ_CSC_RGB2YUV_709_FULL, /* RGB FULL->YCbCr_709 FULL */ + RK_PQ_CSC_YUV2YUV_709_601_FULL, /* YCbCr_709 FULL->YCbCr_601 FULL */ + RK_PQ_CSC_YUV2YUV_601_709_FULL, /* YCbCr_601 FULL->YCbCr_709 FULL */ + RK_PQ_CSC_YUV2YUV_FULL, /* YCbCr FULL->YCbCr FULL */ + RK_PQ_CSC_YUV2YUV_LIMIT2FULL, /* YCbCr LIMIT->YCbCr FULL */ + RK_PQ_CSC_YUV2YUV_601_709_LIMIT2FULL, /* YCbCr 601 LIMIT->YCbCr 709 FULL */ + RK_PQ_CSC_YUV2YUV_709_601_LIMIT2FULL, /* YCbCr 709 LIMIT->YCbCr 601 FULL */ + RK_PQ_CSC_YUV2YUV_FULL2LIMIT, /* YCbCr FULL->YCbCr LIMIT */ + RK_PQ_CSC_YUV2YUV_601_709_FULL2LIMIT, /* YCbCr 601 FULL->YCbCr 709 LIMIT */ + RK_PQ_CSC_YUV2YUV_709_601_FULL2LIMIT, /* YCbCr 709 FULL->YCbCr 601 LIMIT */ + RK_PQ_CSC_YUV2RGBL_601, /* YCbCr_601 LIMIT-> RGB LIMIT */ + RK_PQ_CSC_YUV2RGBL_709, /* YCbCr_709 LIMIT-> RGB LIMIT */ + RK_PQ_CSC_RGBL2YUV_601, /* RGB LIMIT->YCbCr_601 LIMIT */ + RK_PQ_CSC_RGBL2YUV_709, /* RGB LIMIT->YCbCr_709 LIMIT */ + RK_PQ_CSC_YUV2RGBL_601_FULL, /* YCbCr_601 FULL-> RGB LIMIT */ + RK_PQ_CSC_YUV2RGBL_709_FULL, /* YCbCr_709 FULL-> RGB LIMIT */ + RK_PQ_CSC_RGBL2YUV_601_FULL, /* RGB LIMIT->YCbCr_601 FULL */ + RK_PQ_CSC_RGBL2YUV_709_FULL, /* RGB LIMIT->YCbCr_709 FULL */ + RK_PQ_CSC_RGB2RGBL, /* RGB FULL->RGB LIMIT */ + RK_PQ_CSC_RGBL2RGB, /* RGB LIMIT->RGB FULL */ + RK_PQ_CSC_RGBL2RGBL, /* RGB LIMIT->RGB LIMIT */ + RK_PQ_CSC_RGB2RGB, /* RGB FULL->RGB FULL */ + RK_PQ_CSC_YUV2RGB_2020, /* YUV 2020 FULL->RGB 2020 FULL */ + RK_PQ_CSC_RGB2YUV2020_LIMIT2FULL, /* BT2020RGBLIMIT -> BT2020YUVFULL */ + RK_PQ_CSC_RGB2YUV2020_LIMIT, /* BT2020RGBLIMIT -> BT2020YUVLIMIT */ + RK_PQ_CSC_RGB2YUV2020_FULL2LIMIT, /* BT2020RGBFULL -> BT2020YUVLIMIT */ + RK_PQ_CSC_RGB2YUV2020_FULL, /* BT2020RGBFULL -> BT2020YUVFULL */ +}; + +enum color_space_type { + OPTM_CS_E_UNKNOWN = 0, + OPTM_CS_E_ITU_R_BT_709 = 1, + OPTM_CS_E_FCC = 4, + OPTM_CS_E_ITU_R_BT_470_2_BG = 5, + OPTM_CS_E_SMPTE_170_M = 6, + OPTM_CS_E_SMPTE_240_M = 7, + OPTM_CS_E_XV_YCC_709 = OPTM_CS_E_ITU_R_BT_709, + OPTM_CS_E_XV_YCC_601 = 8, + OPTM_CS_E_RGB = 9, + OPTM_CS_E_XV_YCC_2020 = 10, + OPTM_CS_E_RGB_2020 = 11, +}; + +struct rk_pq_csc_coef { + s32 csc_coef00; + s32 csc_coef01; + s32 csc_coef02; + s32 csc_coef10; + s32 csc_coef11; + s32 csc_coef12; + s32 csc_coef20; + s32 csc_coef21; + s32 csc_coef22; +}; + +struct rk_pq_csc_ventor { + s32 csc_offset0; + s32 csc_offset1; + s32 csc_offset2; +}; + +struct rk_pq_csc_dc_coef { + s32 csc_in_dc0; + s32 csc_in_dc1; + s32 csc_in_dc2; + s32 csc_out_dc0; + s32 csc_out_dc1; + s32 csc_out_dc2; +}; + +/* color space param */ +struct rk_csc_colorspace_info { + enum color_space_type input_color_space; + enum color_space_type output_color_space; + bool in_full_range; + bool out_full_range; +}; + +struct rk_csc_mode_coef { + enum rk_pq_csc_mode csc_mode; + char c_csc_comment[PQ_CSC_MODE_COEF_COMMENT_LEN]; + const struct rk_pq_csc_coef *pst_csc_coef; + const struct rk_pq_csc_dc_coef *pst_csc_dc_coef; + struct rk_csc_colorspace_info st_csc_color_info; +}; + +/* + *CSC matrix + */ +/* xv_ycc BT.601 limit(i.e. SD) -> RGB full */ +static const struct rk_pq_csc_coef rk_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_full = { + 1196, 0, 1639, + 1196, -402, -835, + 1196, 2072, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_full = { + -64, -512, -512, + 0, 0, 0 +}; + +/* BT.709 limit(i.e. HD) -> RGB full */ +static const struct rk_pq_csc_coef rk_csc_table_hdy_cb_cr_limit_to_rgb_full = { + 1196, 0, 1841, + 1196, -219, -547, + 1196, 2169, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_limit_to_rgb_full = { + -64, -512, -512, + 0, 0, 0 +}; + +/* RGB full-> YUV601 (i.e. SD) limit */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_to_xv_yccsdy_cb_cr = { + 262, 515, 100, + -151, -297, 448, + 448, -376, -73 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_to_xv_yccsdy_cb_cr = { + 0, 0, 0, + 64, 512, 512 +}; + +/* RGB full-> YUV709 (i.e. SD) limit */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_to_hdy_cb_cr = { + 186, 627, 63, + -103, -346, 448, + 448, -407, -41 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_to_hdy_cb_cr = { + 0, 0, 0, + 64, 512, 512 +}; + +/* BT.709 (i.e. HD) -> to xv_ycc BT.601 (i.e. SD) */ +static const struct rk_pq_csc_coef rk_csc_table_hdy_cb_cr_to_xv_yccsdy_cb_cr = { + 1024, 104, 201, + 0, 1014, -113, + 0, -74, 1007 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_to_xv_yccsdy_cb_cr = { + -64, -512, -512, + 64, 512, 512 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full = { + 0, -512, -512, + 0, 512, 512 +}; + +/* xv_ycc BT.601 (i.e. SD) -> to BT.709 (i.e. HD) */ +static const struct rk_pq_csc_coef rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr = { + 1024, -121, -218, + 0, 1043, 117, + 0, 77, 1050 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr = { + -64, -512, -512, + 64, 512, 512 +}; + +/* xv_ycc BT.601 full(i.e. SD) -> RGB full */ +static const struct rk_pq_csc_coef rk_csc_table_xv_yccsdy_cb_cr_to_rgb_full = { + 1024, 0, 1436, + 1024, -352, -731, + 1024, 1815, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_xv_yccsdy_cb_cr_to_rgb_full = { + 0, -512, -512, + 0, 0, 0 +}; + +/* BT.709 full(i.e. HD) -> RGB full */ +static const struct rk_pq_csc_coef rk_csc_table_hdy_cb_cr_to_rgb_full = { + 1024, 0, 1613, + 1024, -192, -479, + 1024, 1900, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_to_rgb_full = { + 0, -512, -512, + 0, 0, 0 +}; + +/* RGB full-> YUV601 full(i.e. SD) */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_to_xv_yccsdy_cb_cr_full = { + 306, 601, 117, + -173, -339, 512, + 512, -429, -83 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_to_xv_yccsdy_cb_cr_full = { + 0, 0, 0, + 0, 512, 512 +}; + +/* RGB full-> YUV709 full (i.e. SD) */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_to_hdy_cb_cr_full = { + 218, 732, 74, + -117, -395, 512, + 512, -465, -47 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_to_hdy_cb_cr_full = { + 0, 0, 0, + 0, 512, 512 +}; + +/* limit -> full */ +static const struct rk_pq_csc_coef rk_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full = { + 1196, 0, 0, + 0, 1169, 0, + 0, 0, 1169 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full = { + -64, -512, -512, + 0, 512, 512 +}; + +/* 601 limit -> 709 full */ +static const struct rk_pq_csc_coef rk_csc_table_identity_601_limit_to_709_full = { + 1196, -138, -249, + 0, 1191, 134, + 0, 88, 1199 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_601_limit_to_709_full = { + -64, -512, -512, + 0, 512, 512 +}; + +/* 709 limit -> 601 full */ +static const struct rk_pq_csc_coef rk_csc_table_identity_709_limit_to_601_full = { + 1196, 119, 229, + 0, 1157, -129, + 0, -85, 1150 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_709_limit_to_601_full = { + -64, -512, -512, + 0, 512, 512 +}; + +/* full -> limit */ +static const struct rk_pq_csc_coef rk_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit = { + 877, 0, 0, + 0, 897, 0, + 0, 0, 897 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit = { + 0, -512, -512, + 64, 512, 512 +}; + +/* 601 full -> 709 limit */ +static const struct rk_pq_csc_coef rk_csc_table_identity_y_cb_cr_601_full_to_y_cb_cr_709_limit = { + 877, -106, -191, + 0, 914, 103, + 0, 67, 920 +}; + +static const struct rk_pq_csc_dc_coef +rk_dc_csc_table_identity_y_cb_cr_601_full_to_y_cb_cr_709_limit = { + 0, -512, -512, + 64, 512, 512 +}; + +/* 709 full -> 601 limit */ +static const struct rk_pq_csc_coef rk_csc_table_identity_y_cb_cr_709_full_to_y_cb_cr_601_limit = { + 877, 91, 176, + 0, 888, -99, + 0, -65, 882 +}; + +static const struct rk_pq_csc_dc_coef +rk_dc_csc_table_identity_y_cb_cr_709_full_to_y_cb_cr_601_limit = { + 0, -512, -512, + 64, 512, 512 +}; + +/* xv_ycc BT.601 limit(i.e. SD) -> RGB limit */ +static const struct rk_pq_csc_coef rk_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_limit = { + 1024, 0, 1404, + 1024, -344, -715, + 1024, 1774, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_limit = { + -64, -512, -512, + 64, 64, 64 +}; + +/* BT.709 limit(i.e. HD) -> RGB limit */ +static const struct rk_pq_csc_coef rk_csc_table_hdy_cb_cr_limit_to_rgb_limit = { + 1024, 0, 1577, + 1024, -188, -469, + 1024, 1858, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_limit_to_rgb_limit = { + -64, -512, -512, + 64, 64, 64 +}; + +/* RGB limit-> YUV601 (i.e. SD) limit */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_limit_to_xv_yccsdy_cb_cr = { + 306, 601, 117, + -177, -347, 524, + 524, -439, -85 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_limit_to_xv_yccsdy_cb_cr = { + -64, -64, -64, + 64, 512, 512 +}; + +/* RGB limit -> YUV709 (i.e. SD) limit */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_limit_to_hdy_cb_cr = { + 218, 732, 74, + -120, -404, 524, + 524, -476, -48 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_limit_to_hdy_cb_cr = { + -64, -64, -64, + 64, 512, 512 +}; + +/* xv_ycc BT.601 full(i.e. SD) -> RGB limit */ +static const struct rk_pq_csc_coef rk_csc_table_xv_yccsdy_cb_cr_to_rgb_limit = { + 877, 0, 1229, + 877, -302, -626, + 877, 1554, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_xv_yccsdy_cb_cr_to_rgb_limit = { + 0, -512, -512, + 64, 64, 64 +}; + +/* BT.709 full(i.e. HD) -> RGB limit */ +static const struct rk_pq_csc_coef rk_csc_table_hdy_cb_cr_to_rgb_limit = { + 877, 0, 1381, + 877, -164, -410, + 877, 1627, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_hdy_cb_cr_to_rgb_limit = { + 0, -512, -512, + 64, 64, 64 +}; + +/* RGB limit-> YUV601 full(i.e. SD) */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_limit_to_xv_yccsdy_cb_cr_full = { + 358, 702, 136, + -202, -396, 598, + 598, -501, -97 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_limit_to_xv_yccsdy_cb_cr_full = { + -64, -64, -64, + 0, 512, 512 +}; + +/* RGB limit-> YUV709 full (i.e. SD) */ +static const struct rk_pq_csc_coef rk_csc_table_rgb_limit_to_hdy_cb_cr_full = { + 254, 855, 86, + -137, -461, 598, + 598, -543, -55 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_rgb_limit_to_hdy_cb_cr_full = { + -64, -64, -64, + 0, 512, 512 +}; + +/* RGB full -> RGB limit */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_to_rgb_limit = { + 877, 0, 0, + 0, 877, 0, + 0, 0, 877 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_to_rgb_limit = { + 0, 0, 0, + 64, 64, 64 +}; + +/* RGB limit -> RGB full */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_limit_to_rgb = { + 1196, 0, 0, + 0, 1196, 0, + 0, 0, 1196 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_limit_to_rgb = { + -64, -64, -64, + 0, 0, 0 +}; + +/* RGB limit/full -> RGB limit/full */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_to_rgb = { + 1024, 0, 0, + 0, 1024, 0, + 0, 0, 1024 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_to_rgb1 = { + -64, -64, -64, + 64, 64, 64 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_to_rgb2 = { + 0, 0, 0, + 0, 0, 0 +}; + +static const struct rk_pq_csc_coef rk_csc_table_identity_yuv_to_rgb_2020 = { + 1024, 0, 1510, + 1024, -169, -585, + 1024, 1927, 0 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_yuv_to_rgb_2020 = { + 0, -512, -512, + 0, 0, 0 +}; + +/* 2020 RGB LIMIT ->YUV LIMIT */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_limit_to_yuv_limit_2020 = { + 269, 694, 61, + -146, -377, 524, + 524, -482, -42 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_limit_to_yuv_limit_2020 = { + -64, -64, -64, + 64, 512, 512 +}; + +/* 2020 RGB LIMIT ->YUV FULL */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_limit_to_yuv_full_2020 = { + 314, 811, 71, + -167, -431, 598, + 598, -550, -48 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_limit_to_yuv_full_2020 = { + -64, -64, -64, + 0, 512, 512 +}; + +/* 2020 RGB FULL ->YUV LIMIT */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_full_to_yuv_limit_2020 = { + 230, 595, 52, + -125, -323, 448, + 448, -412, -36 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_full_to_yuv_limit_2020 = { + 0, 0, 0, + 64, 512, 512 +}; + +/* 2020 RGB FULL ->YUV FULL */ +static const struct rk_pq_csc_coef rk_csc_table_identity_rgb_full_to_yuv_full_2020 = { + 269, 694, 61, + -143, -369, 512, + 512, -471, -41 +}; + +static const struct rk_pq_csc_dc_coef rk_dc_csc_table_identity_rgb_full_to_yuv_full_2020 = { + 0, 0, 0, + 0, 512, 512 +}; + +/* identity matrix */ +static const struct rk_pq_csc_coef rk_csc_table_identity_y_cb_cr_to_y_cb_cr = { + 1024, 0, 0, + 0, 1024, 0, + 0, 0, 1024 +}; + +/* + *CSC Param Struct + */ +static const struct rk_csc_mode_coef g_mode_csc_coef[] = { + { + RK_PQ_CSC_YUV2RGB_601, "YUV601 L->RGB F", + &rk_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_full, + &rk_dc_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_RGB, false, true + } + }, + { + RK_PQ_CSC_YUV2RGB_709, "YUV709 L->RGB F", + &rk_csc_table_hdy_cb_cr_limit_to_rgb_full, + &rk_dc_csc_table_hdy_cb_cr_limit_to_rgb_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_RGB, false, true + } + }, + { + RK_PQ_CSC_RGB2YUV_601, "RGB F->YUV601 L", + &rk_csc_table_rgb_to_xv_yccsdy_cb_cr, + &rk_dc_csc_table_rgb_to_xv_yccsdy_cb_cr, + { + OPTM_CS_E_RGB, OPTM_CS_E_XV_YCC_601, true, false + } + }, + { + RK_PQ_CSC_RGB2YUV_709, "RGB F->YUV709 L", + &rk_csc_table_rgb_to_hdy_cb_cr, + &rk_dc_csc_table_rgb_to_hdy_cb_cr, + { + OPTM_CS_E_RGB, OPTM_CS_E_ITU_R_BT_709, true, false + } + }, + { + RK_PQ_CSC_YUV2YUV_709_601, "YUV709 L->YUV601 L", + &rk_csc_table_hdy_cb_cr_to_xv_yccsdy_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_to_xv_yccsdy_cb_cr, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_XV_YCC_601, false, false + } + }, + { + RK_PQ_CSC_YUV2YUV_601_709, "YUV601 L->YUV709 L", + &rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_ITU_R_BT_709, false, false + } + }, + { + RK_PQ_CSC_YUV2YUV, "YUV L->YUV L", + &rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_ITU_R_BT_709, false, false + } + }, + { + RK_PQ_CSC_YUV2RGB_601_FULL, "YUV601 F->RGB F", + &rk_csc_table_xv_yccsdy_cb_cr_to_rgb_full, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_rgb_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_RGB, true, true + } + }, + { + RK_PQ_CSC_YUV2RGB_709_FULL, "YUV709 F->RGB F", + &rk_csc_table_hdy_cb_cr_to_rgb_full, + &rk_dc_csc_table_hdy_cb_cr_to_rgb_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_RGB, true, true + } + }, + { + RK_PQ_CSC_RGB2YUV_601_FULL, "RGB F->YUV601 F", + &rk_csc_table_rgb_to_xv_yccsdy_cb_cr_full, + &rk_dc_csc_table_rgb_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_RGB, OPTM_CS_E_XV_YCC_601, true, true + } + }, + { + RK_PQ_CSC_RGB2YUV_709_FULL, "RGB F->YUV709 F", + &rk_csc_table_rgb_to_hdy_cb_cr_full, + &rk_dc_csc_table_rgb_to_hdy_cb_cr_full, + { + OPTM_CS_E_RGB, OPTM_CS_E_ITU_R_BT_709, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_709_601_FULL, "YUV709 F->YUV601 F", + &rk_csc_table_hdy_cb_cr_to_xv_yccsdy_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_XV_YCC_601, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_601_709_FULL, "YUV601 F->YUV709 F", + &rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_ITU_R_BT_709, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL, "YUV F->YUV F", + &rk_csc_table_identity_y_cb_cr_to_y_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_ITU_R_BT_709, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_LIMIT2FULL, "YUV L->YUV F", + &rk_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + &rk_dc_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_ITU_R_BT_709, false, true + } + }, + { + RK_PQ_CSC_YUV2YUV_601_709_LIMIT2FULL, "YUV601 L->YUV709 F", + &rk_csc_table_identity_601_limit_to_709_full, + &rk_dc_csc_table_identity_601_limit_to_709_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_ITU_R_BT_709, false, true + } + }, + { + RK_PQ_CSC_YUV2YUV_709_601_LIMIT2FULL, "YUV709 L->YUV601 F", + &rk_csc_table_identity_709_limit_to_601_full, + &rk_dc_csc_table_identity_709_limit_to_601_full, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_XV_YCC_601, false, true + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL2LIMIT, "YUV F->YUV L", + &rk_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + &rk_dc_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_ITU_R_BT_709, true, false + } + }, + { + RK_PQ_CSC_YUV2YUV_601_709_FULL2LIMIT, "YUV601 F->YUV709 L", + &rk_csc_table_identity_y_cb_cr_601_full_to_y_cb_cr_709_limit, + &rk_dc_csc_table_identity_y_cb_cr_601_full_to_y_cb_cr_709_limit, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_ITU_R_BT_709, true, false + } + }, + { + RK_PQ_CSC_YUV2YUV_709_601_FULL2LIMIT, "YUV709 F->YUV601 L", + &rk_csc_table_identity_y_cb_cr_709_full_to_y_cb_cr_601_limit, + &rk_dc_csc_table_identity_y_cb_cr_709_full_to_y_cb_cr_601_limit, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_XV_YCC_601, true, false + } + }, + { + RK_PQ_CSC_YUV2RGBL_601, "YUV601 L->RGB L", + &rk_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_limit, + &rk_dc_csc_table_xv_yccsdy_cb_cr_limit_to_rgb_limit, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_RGB, false, false + } + }, + { + RK_PQ_CSC_YUV2RGBL_709, "YUV709 L->RGB L", + &rk_csc_table_hdy_cb_cr_limit_to_rgb_limit, + &rk_dc_csc_table_hdy_cb_cr_limit_to_rgb_limit, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_RGB, false, false + } + }, + { + RK_PQ_CSC_RGBL2YUV_601, "RGB L->YUV601 L", + &rk_csc_table_rgb_limit_to_xv_yccsdy_cb_cr, + &rk_dc_csc_table_rgb_limit_to_xv_yccsdy_cb_cr, + { + OPTM_CS_E_RGB, OPTM_CS_E_XV_YCC_601, false, false + } + }, + { + RK_PQ_CSC_RGBL2YUV_709, "RGB L->YUV709 L", + &rk_csc_table_rgb_limit_to_hdy_cb_cr, + &rk_dc_csc_table_rgb_limit_to_hdy_cb_cr, + { + OPTM_CS_E_RGB, OPTM_CS_E_ITU_R_BT_709, false, false + } + }, + { + RK_PQ_CSC_YUV2RGBL_601_FULL, "YUV601 F->RGB L", + &rk_csc_table_xv_yccsdy_cb_cr_to_rgb_limit, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_rgb_limit, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_RGB, true, false + } + }, + { + RK_PQ_CSC_YUV2RGBL_709_FULL, "YUV709 F->RGB L", + &rk_csc_table_hdy_cb_cr_to_rgb_limit, + &rk_dc_csc_table_hdy_cb_cr_to_rgb_limit, + { + OPTM_CS_E_ITU_R_BT_709, OPTM_CS_E_RGB, true, false + } + }, + { + RK_PQ_CSC_RGBL2YUV_601_FULL, "RGB L->YUV601 F", + &rk_csc_table_rgb_limit_to_xv_yccsdy_cb_cr_full, + &rk_dc_csc_table_rgb_limit_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_RGB, OPTM_CS_E_XV_YCC_601, false, true + } + }, + { + RK_PQ_CSC_RGBL2YUV_709_FULL, "RGB L->YUV709 F", + &rk_csc_table_rgb_limit_to_hdy_cb_cr_full, + &rk_dc_csc_table_rgb_limit_to_hdy_cb_cr_full, + { + OPTM_CS_E_RGB, OPTM_CS_E_ITU_R_BT_709, false, true + } + }, + { + RK_PQ_CSC_RGB2RGBL, "RGB F->RGB L", + &rk_csc_table_identity_rgb_to_rgb_limit, + &rk_dc_csc_table_identity_rgb_to_rgb_limit, + { + OPTM_CS_E_RGB, OPTM_CS_E_RGB, true, false + } + }, + { + RK_PQ_CSC_RGBL2RGB, "RGB L->RGB F", + &rk_csc_table_identity_rgb_limit_to_rgb, + &rk_dc_csc_table_identity_rgb_limit_to_rgb, + { + OPTM_CS_E_RGB, OPTM_CS_E_RGB, false, true + } + }, + { + RK_PQ_CSC_RGBL2RGBL, "RGB L->RGB L", + &rk_csc_table_identity_rgb_to_rgb, + &rk_dc_csc_table_identity_rgb_to_rgb1, + { + OPTM_CS_E_RGB, OPTM_CS_E_RGB, false, false + } + }, + { + RK_PQ_CSC_RGB2RGB, "RGB F->RGB F", + &rk_csc_table_identity_rgb_to_rgb, + &rk_dc_csc_table_identity_rgb_to_rgb2, + { + OPTM_CS_E_RGB, OPTM_CS_E_RGB, true, true + } + }, + { + RK_PQ_CSC_YUV2RGB_2020, "YUV2020 F->RGB2020 F", + &rk_csc_table_identity_yuv_to_rgb_2020, + &rk_dc_csc_table_identity_yuv_to_rgb_2020, + { + OPTM_CS_E_XV_YCC_2020, OPTM_CS_E_RGB_2020, true, true + } + }, + { + RK_PQ_CSC_RGB2YUV2020_LIMIT2FULL, "RGB2020 L->YUV2020 F", + &rk_csc_table_identity_rgb_limit_to_yuv_full_2020, + &rk_dc_csc_table_identity_rgb_limit_to_yuv_full_2020, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_XV_YCC_2020, false, true + } + }, + { + RK_PQ_CSC_RGB2YUV2020_LIMIT, "RGB2020 L->YUV2020 L", + &rk_csc_table_identity_rgb_limit_to_yuv_limit_2020, + &rk_dc_csc_table_identity_rgb_limit_to_yuv_limit_2020, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_XV_YCC_2020, false, false + } + }, + { + RK_PQ_CSC_RGB2YUV2020_FULL2LIMIT, "RGB2020 F->YUV2020 L", + &rk_csc_table_identity_rgb_full_to_yuv_limit_2020, + &rk_dc_csc_table_identity_rgb_full_to_yuv_limit_2020, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_XV_YCC_2020, true, false + } + }, + { + RK_PQ_CSC_RGB2YUV2020_FULL, "RGB2020 F->YUV2020 F", + &rk_csc_table_identity_rgb_full_to_yuv_full_2020, + &rk_dc_csc_table_identity_rgb_full_to_yuv_full_2020, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_XV_YCC_2020, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV, "YUV 601 L->YUV 601 L", + &rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_XV_YCC_601, false, false + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL, "YUV 601 F->YUV 601 F", + &rk_csc_table_identity_y_cb_cr_to_y_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_XV_YCC_601, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_LIMIT2FULL, "YUV 601 L->YUV 601 F", + &rk_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + &rk_dc_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_XV_YCC_601, false, true + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL2LIMIT, "YUV 601 F->YUV 601 L", + &rk_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + &rk_dc_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + { + OPTM_CS_E_XV_YCC_601, OPTM_CS_E_XV_YCC_601, true, false + } + }, + { + RK_PQ_CSC_YUV2YUV, "YUV 2020 L->YUV 2020 L", + &rk_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + &rk_dc_csc_table_xv_yccsdy_cb_cr_to_hdy_cb_cr, + { + OPTM_CS_E_XV_YCC_2020, OPTM_CS_E_XV_YCC_2020, false, false + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL, "YUV 2020 F->YUV 2020 F", + &rk_csc_table_identity_y_cb_cr_to_y_cb_cr, + &rk_dc_csc_table_hdy_cb_cr_full_to_xv_yccsdy_cb_cr_full, + { + OPTM_CS_E_XV_YCC_2020, OPTM_CS_E_XV_YCC_2020, true, true + } + }, + { + RK_PQ_CSC_YUV2YUV_LIMIT2FULL, "YUV 2020 L->YUV 2020 F", + &rk_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + &rk_dc_csc_table_identity_y_cb_cr_limit_to_y_cb_cr_full, + { + OPTM_CS_E_XV_YCC_2020, OPTM_CS_E_XV_YCC_2020, false, true + } + }, + { + RK_PQ_CSC_YUV2YUV_FULL2LIMIT, "YUV 2020 F->YUV 2020 L", + &rk_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + &rk_dc_csc_table_identity_y_cb_cr_full_to_y_cb_cr_limit, + { + OPTM_CS_E_XV_YCC_2020, OPTM_CS_E_XV_YCC_2020, true, false + } + }, + { + RK_PQ_CSC_RGB2RGBL, "RGB 2020 F->RGB 2020 L", + &rk_csc_table_identity_rgb_to_rgb_limit, + &rk_dc_csc_table_identity_rgb_to_rgb_limit, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_RGB_2020, true, false + } + }, + { + RK_PQ_CSC_RGBL2RGB, "RGB 2020 L->RGB 2020 F", + &rk_csc_table_identity_rgb_limit_to_rgb, + &rk_dc_csc_table_identity_rgb_limit_to_rgb, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_RGB_2020, false, true + } + }, + { + RK_PQ_CSC_RGBL2RGBL, "RGB 2020 L->RGB 2020 L", + &rk_csc_table_identity_rgb_to_rgb, + &rk_dc_csc_table_identity_rgb_to_rgb1, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_RGB_2020, false, false + } + }, + { + RK_PQ_CSC_RGB2RGB, "RGB 2020 F->RGB 2020 F", + &rk_csc_table_identity_rgb_to_rgb, + &rk_dc_csc_table_identity_rgb_to_rgb2, + { + OPTM_CS_E_RGB_2020, OPTM_CS_E_RGB_2020, true, true + } + }, +}; + +enum vop_csc_format { + CSC_BT601L, + CSC_BT709L, + CSC_BT601F, + CSC_BT2020, + CSC_BT709L_13BIT, + CSC_BT709F_13BIT, + CSC_BT2020L_13BIT, + CSC_BT2020F_13BIT, +}; + +enum vop_csc_mode { + CSC_RGB, + CSC_YUV, +}; + +struct csc_mapping { + enum vop_csc_format csc_format; + enum color_space_type rgb_color_space; + enum color_space_type yuv_color_space; + bool rgb_full_range; + bool yuv_full_range; +}; + +static const struct csc_mapping csc_mapping_table[] = { + { + CSC_BT601L, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_601, + true, + false, + }, + { + CSC_BT709L, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_709, + true, + false, + }, + { + CSC_BT601F, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_601, + true, + true, + }, + { + CSC_BT2020, + OPTM_CS_E_RGB_2020, + OPTM_CS_E_XV_YCC_2020, + true, + true, + }, + { + CSC_BT709L_13BIT, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_709, + true, + false, + }, + { + CSC_BT709F_13BIT, + OPTM_CS_E_RGB, + OPTM_CS_E_XV_YCC_709, + true, + true, + }, + { + CSC_BT2020L_13BIT, + OPTM_CS_E_RGB_2020, + OPTM_CS_E_XV_YCC_2020, + true, + false, + }, + { + CSC_BT2020F_13BIT, + OPTM_CS_E_RGB_2020, + OPTM_CS_E_XV_YCC_2020, + true, + true, + }, +}; + +static const struct rk_pq_csc_coef r2y_for_y2y = { + 306, 601, 117, + -151, -296, 446, + 630, -527, -102, +}; + +static const struct rk_pq_csc_coef y2r_for_y2y = { + 1024, -0, 1167, + 1024, -404, -594, + 1024, 2081, -1, +}; + +static const struct rk_pq_csc_coef rgb_input_swap_matrix = { + 0, 0, 1, + 1, 0, 0, + 0, 1, 0, +}; + +static const struct rk_pq_csc_coef yuv_output_swap_matrix = { + 0, 0, 1, + 1, 0, 0, + 0, 1, 0, +}; + +static bool is_rgb_format(u64 format) +{ + switch (format) { + case BUS_FMT_YUV420: + case BUS_FMT_YUV422: + case BUS_FMT_YUV444: + return false; + case BUS_FMT_RGB: + default: + return true; + } +} + +struct post_csc_coef { + s32 csc_coef00; + s32 csc_coef01; + s32 csc_coef02; + s32 csc_coef10; + s32 csc_coef11; + s32 csc_coef12; + s32 csc_coef20; + s32 csc_coef21; + s32 csc_coef22; + + s32 csc_dc0; + s32 csc_dc1; + s32 csc_dc2; + + u32 range_type; +}; + +static int csc_get_mode_index(int post_csc_mode, bool is_input_yuv, bool is_output_yuv) +{ + const struct rk_csc_colorspace_info *colorspace_info; + enum color_space_type input_color_space; + enum color_space_type output_color_space; + bool is_input_full_range; + bool is_output_full_range; + int i; + + for (i = 0; i < ARRAY_SIZE(csc_mapping_table); i++) { + if (post_csc_mode == csc_mapping_table[i].csc_format) { + input_color_space = is_input_yuv ? csc_mapping_table[i].yuv_color_space : + csc_mapping_table[i].rgb_color_space; + is_input_full_range = is_input_yuv ? csc_mapping_table[i].yuv_full_range : + csc_mapping_table[i].rgb_full_range; + output_color_space = is_output_yuv ? csc_mapping_table[i].yuv_color_space : + csc_mapping_table[i].rgb_color_space; + is_output_full_range = is_output_yuv ? csc_mapping_table[i].yuv_full_range : + csc_mapping_table[i].rgb_full_range; + break; + } + } + if (i >= ARRAY_SIZE(csc_mapping_table)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(g_mode_csc_coef); i++) { + colorspace_info = &g_mode_csc_coef[i].st_csc_color_info; + if (colorspace_info->input_color_space == input_color_space && + colorspace_info->output_color_space == output_color_space && + colorspace_info->in_full_range == is_input_full_range && + colorspace_info->out_full_range == is_output_full_range) + return i; + } + + return -EINVAL; +} + +static void csc_matrix_ventor_multiply(struct rk_pq_csc_ventor *dst, + const struct rk_pq_csc_coef *m0, + const struct rk_pq_csc_ventor *v0) +{ + dst->csc_offset0 = m0->csc_coef00 * v0->csc_offset0 + + m0->csc_coef01 * v0->csc_offset1 + + m0->csc_coef02 * v0->csc_offset2; + + dst->csc_offset1 = m0->csc_coef10 * v0->csc_offset0 + + m0->csc_coef11 * v0->csc_offset1 + + m0->csc_coef12 * v0->csc_offset2; + + dst->csc_offset2 = m0->csc_coef20 * v0->csc_offset0 + + m0->csc_coef21 * v0->csc_offset1 + + m0->csc_coef22 * v0->csc_offset2; +} + +static inline s32 pq_csc_simple_round(s32 x, s32 n) +{ + s32 value = 0; + + if (n == 0) + return x; + + value = (abs(x) + (1 << (n - 1))) >> (n); + return (((x) >= 0) ? value : -value); +} + +static int csc_calc_default_output_coef(const struct rk_csc_mode_coef *csc_mode_cfg, + struct rk_pq_csc_coef *out_matrix, + struct rk_pq_csc_ventor *out_dc) +{ + const struct rk_pq_csc_coef *csc_coef; + const struct rk_pq_csc_dc_coef *csc_dc_coef; + struct rk_pq_csc_ventor dc_in_ventor; + struct rk_pq_csc_ventor dc_out_ventor; + struct rk_pq_csc_ventor v; + + csc_coef = csc_mode_cfg->pst_csc_coef; + csc_dc_coef = csc_mode_cfg->pst_csc_dc_coef; + + out_matrix->csc_coef00 = csc_coef->csc_coef00; + out_matrix->csc_coef01 = csc_coef->csc_coef01; + out_matrix->csc_coef02 = csc_coef->csc_coef02; + out_matrix->csc_coef10 = csc_coef->csc_coef10; + out_matrix->csc_coef11 = csc_coef->csc_coef11; + out_matrix->csc_coef12 = csc_coef->csc_coef12; + out_matrix->csc_coef20 = csc_coef->csc_coef20; + out_matrix->csc_coef21 = csc_coef->csc_coef21; + out_matrix->csc_coef22 = csc_coef->csc_coef22; + + dc_in_ventor.csc_offset0 = csc_dc_coef->csc_in_dc0; + dc_in_ventor.csc_offset1 = csc_dc_coef->csc_in_dc1; + dc_in_ventor.csc_offset2 = csc_dc_coef->csc_in_dc2; + dc_out_ventor.csc_offset0 = csc_dc_coef->csc_out_dc0; + dc_out_ventor.csc_offset1 = csc_dc_coef->csc_out_dc1; + dc_out_ventor.csc_offset2 = csc_dc_coef->csc_out_dc2; + + csc_matrix_ventor_multiply(&v, csc_coef, &dc_in_ventor); + out_dc->csc_offset0 = v.csc_offset0 + dc_out_ventor.csc_offset0 * + PQ_CSC_SIMPLE_MAT_PARAM_FIX_NUM; + out_dc->csc_offset1 = v.csc_offset1 + dc_out_ventor.csc_offset1 * + PQ_CSC_SIMPLE_MAT_PARAM_FIX_NUM; + out_dc->csc_offset2 = v.csc_offset2 + dc_out_ventor.csc_offset2 * + PQ_CSC_SIMPLE_MAT_PARAM_FIX_NUM; + + return 0; +} + +static int vop2_convert_csc_mode(int csc_mode, int bit_depth) +{ + switch (csc_mode) { + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + return CSC_BT601L; + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SMPTE240M: + case V4L2_COLORSPACE_DEFAULT: + if (bit_depth == CSC_13BIT_DEPTH) + return CSC_BT709L_13BIT; + else + return CSC_BT709L; + case V4L2_COLORSPACE_JPEG: + return CSC_BT601F; + case V4L2_COLORSPACE_BT2020: + if (bit_depth == CSC_13BIT_DEPTH) + return CSC_BT2020L_13BIT; + else + return CSC_BT2020; + case V4L2_COLORSPACE_BT709F: + if (bit_depth == CSC_10BIT_DEPTH) + return CSC_BT601F; + else + return CSC_BT709F_13BIT; + case V4L2_COLORSPACE_BT2020F: + if (bit_depth == CSC_10BIT_DEPTH) + return CSC_BT601F; + else + return CSC_BT2020F_13BIT; + default: + return CSC_BT709L; + } +} + +static int rockchip_calc_post_csc(struct post_csc_coef *csc_simple_coef, + int csc_mode, bool is_input_yuv, bool is_output_yuv) +{ + int ret = 0; + struct rk_pq_csc_coef out_matrix; + struct rk_pq_csc_ventor out_dc; + const struct rk_csc_mode_coef *csc_mode_cfg; + int bit_num = PQ_CSC_SIMPLE_MAT_PARAM_FIX_BIT_WIDTH; + + ret = csc_get_mode_index(csc_mode, is_input_yuv, is_output_yuv); + if (ret < 0) + return ret; + + csc_mode_cfg = &g_mode_csc_coef[ret]; + + ret = csc_calc_default_output_coef(csc_mode_cfg, &out_matrix, &out_dc); + + csc_simple_coef->csc_coef00 = out_matrix.csc_coef00; + csc_simple_coef->csc_coef01 = out_matrix.csc_coef01; + csc_simple_coef->csc_coef02 = out_matrix.csc_coef02; + csc_simple_coef->csc_coef10 = out_matrix.csc_coef10; + csc_simple_coef->csc_coef11 = out_matrix.csc_coef11; + csc_simple_coef->csc_coef12 = out_matrix.csc_coef12; + csc_simple_coef->csc_coef20 = out_matrix.csc_coef20; + csc_simple_coef->csc_coef21 = out_matrix.csc_coef21; + csc_simple_coef->csc_coef22 = out_matrix.csc_coef22; + csc_simple_coef->csc_dc0 = out_dc.csc_offset0; + csc_simple_coef->csc_dc1 = out_dc.csc_offset1; + csc_simple_coef->csc_dc2 = out_dc.csc_offset2; + + csc_simple_coef->csc_dc0 = pq_csc_simple_round(csc_simple_coef->csc_dc0, bit_num); + csc_simple_coef->csc_dc1 = pq_csc_simple_round(csc_simple_coef->csc_dc1, bit_num); + csc_simple_coef->csc_dc2 = pq_csc_simple_round(csc_simple_coef->csc_dc2, bit_num); + csc_simple_coef->range_type = csc_mode_cfg->st_csc_color_info.out_full_range; + + return ret; +} + static void calc_dsp_frm_hst_vst(const struct rk628_display_mode *src, const struct rk628_display_mode *dst, u32 *dsp_frame_hst, @@ -94,6 +1358,10 @@ static void rk628_post_process_scaler_init(struct rk628 *rk628, u32 dst_vsync_len, dst_vback_porch, dst_vfront_porch, dst_vactive; u32 src_hactive; u32 src_vactive; + int gvi_offset = 0; + + if (rk628->version == RK628F_VERSION && rk628->gvi.division_mode) + gvi_offset = 4; src_hactive = src->hdisplay; src_vactive = src->vdisplay; @@ -113,8 +1381,8 @@ static void rk628_post_process_scaler_init(struct rk628 *rk628, dst_vactive + dst_vfront_porch; dsp_hs_end = dst_hsync_len; dsp_vs_end = dst_vsync_len; - dsp_hbor_end = dst_hsync_len + dst_hback_porch + dst_hactive; - dsp_hbor_st = dst_hsync_len + dst_hback_porch; + dsp_hbor_end = dst_hsync_len + dst_hback_porch + dst_hactive - gvi_offset; + dsp_hbor_st = dst_hsync_len + dst_hback_porch - gvi_offset; dsp_vbor_end = dst_vsync_len + dst_vback_porch + dst_vactive; dsp_vbor_st = dst_vsync_len + dst_vback_porch; dsp_hact_st = dsp_hbor_st + bor_left; @@ -194,6 +1462,64 @@ static void rk628_post_process_scaler_init(struct rk628 *rk628, DSP_VBOR_ST(dsp_vbor_st)); } +static int rk628_scaler_color_bar_show(struct seq_file *s, void *data) +{ + seq_puts(s, " Enable horizontal color bar:\n"); + seq_puts(s, " example: echo 1 > /sys/kernel/debug/rk628/2-0050/scaler_color_bar\n"); + seq_puts(s, " Enable vertical color bar:\n"); + seq_puts(s, " example: echo 2 > /sys/kernel/debug/rk628/2-0050/scaler_color_bar\n"); + seq_puts(s, " Disable color bar:\n"); + seq_puts(s, " example: echo 0 > /sys/kernel/debug/rk628/2-0050/scaler_color_bar\n"); + + return 0; +} + +static int rk628_scaler_color_bar_open(struct inode *inode, struct file *file) +{ + return single_open(file, rk628_scaler_color_bar_show, inode->i_private); +} + +static ssize_t rk628_scaler_color_bar_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct rk628 *rk628 = ((struct seq_file *)file->private_data)->private; + u8 mode; + + if (kstrtou8_from_user(ubuf, len, 0, &mode)) + return -EFAULT; + + switch (mode) { + case 0: + rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_COLOR_BAR_EN(0)); + break; + case 1: + rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_COLOR_BAR_EN(1)); + rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_COLOR_VER_EN(0)); + break; + case 2: + default: + rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_COLOR_BAR_EN(1)); + rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_COLOR_VER_EN(1)); + } + + return len; +} + +static const struct file_operations rk628_scaler_color_bar_fops = { + .owner = THIS_MODULE, + .open = rk628_scaler_color_bar_open, + .read = seq_read, + .write = rk628_scaler_color_bar_write, + .llseek = seq_lseek, + .release = single_release, +}; + +void rk628_post_process_create_debugfs_file(struct rk628 *rk628) +{ + debugfs_create_file("scaler_color_bar", 0600, rk628->debug_dir, + rk628, &rk628_scaler_color_bar_fops); +} + void rk628_post_process_init(struct rk628 *rk628) { struct rk628_display_mode *src = &rk628->src_mode; @@ -201,8 +1527,8 @@ void rk628_post_process_init(struct rk628 *rk628) u64 dst_rate, src_rate; src_rate = src->clock * 1000; - dst_rate = src_rate * dst->vdisplay * dst->htotal; - do_div(dst_rate, (src->vdisplay * src->htotal)); + dst_rate = src_rate * dst->vtotal * dst->htotal; + do_div(dst_rate, (src->vtotal * src->htotal)); do_div(dst_rate, 1000); dev_info(rk628->dev, "src %dx%d clock:%d\n", src->hdisplay, src->vdisplay, src->clock); @@ -213,7 +1539,7 @@ void rk628_post_process_init(struct rk628 *rk628) rk628_cru_clk_set_rate(rk628, CGU_CLK_RX_READ, src->clock * 1000); rk628_cru_clk_set_rate(rk628, CGU_SCLK_VOP, dst_rate * 1000); - if (rk628->output_mode == OUTPUT_MODE_HDMI) { + if (rk628_output_is_hdmi(rk628)) { rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_VSYNC_POL_MASK, SW_VSYNC_POL(rk628->sync_pol)); rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_HSYNC_POL_MASK, @@ -234,6 +1560,12 @@ void rk628_post_process_init(struct rk628 *rk628) static void rk628_post_process_csc(struct rk628 *rk628) { enum bus_format in_fmt, out_fmt; + struct post_csc_coef csc_coef = {}; + bool is_input_yuv, is_output_yuv; + u32 color_space = V4L2_COLORSPACE_DEFAULT; + u32 csc_mode; + u32 val; + int range_type; in_fmt = rk628_get_input_bus_format(rk628); out_fmt = rk628_get_output_bus_format(rk628); @@ -250,10 +1582,41 @@ static void rk628_post_process_csc(struct rk628 *rk628) return; } - if (in_fmt == BUS_FMT_RGB) - rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_R2Y_EN(1)); - else if (out_fmt == BUS_FMT_RGB) - rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_Y2R_EN(1)); + if (rk628->version == RK628D_VERSION) { + if (in_fmt == BUS_FMT_RGB) + rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_R2Y_EN(1)); + else if (out_fmt == BUS_FMT_RGB) + rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_Y2R_EN(1)); + } else { + csc_mode = vop2_convert_csc_mode(color_space, CSC_13BIT_DEPTH); + + is_input_yuv = !is_rgb_format(in_fmt); + is_output_yuv = !is_rgb_format(out_fmt); + rockchip_calc_post_csc(&csc_coef, csc_mode, is_input_yuv, is_output_yuv); + + val = ((csc_coef.csc_coef01 & 0xffff) << 16) | (csc_coef.csc_coef00 & 0xffff); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_COE01_COE00, val); + + val = ((csc_coef.csc_coef10 & 0xffff) << 16) | (csc_coef.csc_coef02 & 0xffff); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_COE10_COE02, val); + + val = ((csc_coef.csc_coef12 & 0xffff) << 16) | (csc_coef.csc_coef11 & 0xffff); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_COE12_COE11, val); + + val = ((csc_coef.csc_coef21 & 0xffff) << 16) | (csc_coef.csc_coef20 & 0xffff); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_COE21_COE20, val); + + rk628_i2c_write(rk628, GRF_CSC_MATRIX_COE22, csc_coef.csc_coef22); + + rk628_i2c_write(rk628, GRF_CSC_MATRIX_OFFSET0, csc_coef.csc_dc0); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_OFFSET1, csc_coef.csc_dc1); + rk628_i2c_write(rk628, GRF_CSC_MATRIX_OFFSET2, csc_coef.csc_dc2); + + range_type = csc_coef.range_type ? 0 : 1; + range_type <<= is_input_yuv ? 0 : 1; + val = SW_Y2R_MODE(range_type) | SW_FROM_CSC_MATRIX_EN(1); + rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, val); + } } void rk628_post_process_enable(struct rk628 *rk628) diff --git a/drivers/misc/rk628/rk628_post_process.h b/drivers/misc/rk628/rk628_post_process.h index 62a7fd05f8ff..3b0b2b761af8 100644 --- a/drivers/misc/rk628/rk628_post_process.h +++ b/drivers/misc/rk628/rk628_post_process.h @@ -11,5 +11,5 @@ void rk628_post_process_init(struct rk628 *rk628); void rk628_post_process_enable(struct rk628 *rk628); void rk628_post_process_disable(struct rk628 *rk628); - +void rk628_post_process_create_debugfs_file(struct rk628 *rk628); #endif diff --git a/drivers/misc/rk628/rk628_rgb.c b/drivers/misc/rk628/rk628_rgb.c index 23c8d25eec6b..8cbe0bff3ec7 100644 --- a/drivers/misc/rk628/rk628_rgb.c +++ b/drivers/misc/rk628/rk628_rgb.c @@ -5,46 +5,185 @@ * Author: Guochun Huang */ +#include #include "rk628.h" #include "rk628_cru.h" #include "rk628_config.h" #include "panel.h" +#include "rk628_rgb.h" -void rk628_rgb_decoder_enable(struct rk628 *rk628) +int rk628_rgb_parse(struct rk628 *rk628, struct device_node *rgb_np) { - /* config sw_input_mode RGB */ + int ret = 0; + + rk628->rgb.vccio_rgb = devm_regulator_get_optional(rk628->dev, "vccio-rgb"); + if (IS_ERR(rk628->rgb.vccio_rgb)) + rk628->rgb.vccio_rgb = NULL; + + if ((rk628_input_is_bt1120(rk628) || rk628_output_is_bt1120(rk628)) && + of_property_read_bool(rk628->dev->of_node, "bt1120-dual-edge")) + rk628->rgb.bt1120_dual_edge = true; + + if (rk628_output_is_bt1120(rk628) || rk628_output_is_rgb(rk628)) + ret = rk628_panel_info_get(rk628, rgb_np); + + return ret; +} + +static int rk628_rgb_resolution_show(struct seq_file *s, void *data) +{ + struct rk628 *rk628 = s->private; + u16 width = 0, height = 0; + u32 rgb_rx_eval_time, rgb_rx_clkrate; + u64 ref_clk, pixel_clk; + u32 val; + + rk628_i2c_read(rk628, GRF_RGB_RX_DBG_MEAS0, &val); + rgb_rx_eval_time = (val & RGB_RX_EVAL_TIME_MASK) >> 16; + + rk628_i2c_read(rk628, GRF_RGB_RX_DBG_MEAS2, &val); + rgb_rx_clkrate = val & RGB_RX_CLKRATE_MASK; + + ref_clk = rk628_cru_clk_get_rate(rk628, CGU_CLK_IMODET); + pixel_clk = ref_clk * rgb_rx_clkrate / (rgb_rx_eval_time + 1); + + if (rk628_input_is_rgb(rk628)) { + rk628_i2c_read(rk628, GRF_RGB_RX_DBG_MEAS4, &val); + height = (val >> 16) & 0xffff; + width = val & 0xffff; + } else if (rk628_input_is_bt1120(rk628)) { + rk628_i2c_read(rk628, GRF_SYSTEM_STATUS3, &val); + height = val & DECODER_1120_LAST_LINE_NUM_MASK; + + rk628_i2c_read(rk628, GRF_SYSTEM_STATUS4, &val); + width = val & DECODER_1120_LAST_PIX_NUM_MASK; + } + + seq_printf(s, "%dx%d pclk:%llu\n", width, height, pixel_clk); + + return 0; +} + +static int rk628_rgb_resolution_open(struct inode *inode, struct file *file) +{ + return single_open(file, rk628_rgb_resolution_show, inode->i_private); +} + +static const struct file_operations rk628_rgb_resolution_fops = { + .owner = THIS_MODULE, + .open = rk628_rgb_resolution_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void rk628_rgb_decoder_create_debugfs_file(struct rk628 *rk628) +{ + if (rk628->version == RK628D_VERSION) + return; + + if (rk628_input_is_rgb(rk628) || rk628_input_is_bt1120(rk628)) + debugfs_create_file("rgb_resolution", 0400, rk628->debug_dir, + rk628, &rk628_rgb_resolution_fops); +} + +static void rk628_rgb_decoder_enable(struct rk628 *rk628) +{ + /* config sw_input_mode RGB */ rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_INPUT_MODE_MASK, SW_INPUT_MODE(INPUT_MODE_RGB)); + if (rk628->version == RK628F_VERSION) { + rk628_i2c_update_bits(rk628, GRF_RGB_RX_DBG_MEAS0, + RGB_RX_MODET_EN | RGB_RX_DCLK_EN, + RGB_RX_MODET_EN | RGB_RX_DCLK_EN); + rk628_i2c_update_bits(rk628, GRF_RGB_RX_DBG_MEAS3, + RGB_RX_CNT_EN_MASK, RGB_RX_CNT_EN(1)); + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, 0x10000); + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0); + } + + /* pinctrl for vop pin */ + rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff); + rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555); + rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b); +} + +static void rk628_rgb_encoder_enable(struct rk628 *rk628) +{ + int voltage = 0; + u32 d_strength, clk_strength; + u64 dclk_delay; + + rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, + SW_BT_DATA_OEN_MASK | SW_OUTPUT_RGB_MODE_MASK, + SW_OUTPUT_RGB_MODE(OUTPUT_MODE_RGB >> 3)); + + if (rk628->version != RK628F_VERSION) + rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, + SW_DCLK_OUT_INV_EN, SW_DCLK_OUT_INV_EN); + /* pinctrl for vop pin */ rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff); rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555); rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b); + /* + * Under the same drive strength and DCLK delay, the signal behaves + * differently under different voltage power domains. In order to + * center the eye diagram of the signal, have sufficient signal setup + * and hold time, and ensure that the signal does not overshoot, the + * drive strength and DCLK delay need to be set for the power domains + * of different voltages. + */ + if (rk628->rgb.vccio_rgb) + voltage = regulator_get_voltage(rk628->rgb.vccio_rgb); + + switch (voltage) { + case 1800000: + d_strength = 3; + clk_strength = 3; + dclk_delay = 0x10000000; + break; + case 3300000: + d_strength = 1; + clk_strength = 2; + dclk_delay = 0x100000000; + break; + default: + d_strength = 1; + clk_strength = 2; + dclk_delay = 0x100000000; + } + /* rk628: modify IO drive strength for RGB */ - rk628_i2c_write(rk628, GRF_GPIO2A_D0_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2A_D1_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2B_D0_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2B_D1_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2C_D0_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2C_D1_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO3A_D0_CON, 0xffff1011); - rk628_i2c_write(rk628, GRF_GPIO3B_D_CON, 0x10001); + if (rk628->version == RK628F_VERSION) + d_strength = d_strength * 0x1111 | 0xffff0000; + else { + d_strength = 0xffff7777; + clk_strength = 7; + } + + rk628_i2c_write(rk628, GRF_GPIO2A_D0_CON, d_strength); + rk628_i2c_write(rk628, GRF_GPIO2A_D1_CON, d_strength); + rk628_i2c_write(rk628, GRF_GPIO2B_D0_CON, d_strength); + rk628_i2c_write(rk628, GRF_GPIO2B_D1_CON, d_strength); + rk628_i2c_write(rk628, GRF_GPIO2C_D0_CON, d_strength); + rk628_i2c_write(rk628, GRF_GPIO2C_D1_CON, d_strength); + rk628_i2c_write(rk628, GRF_GPIO3A_D0_CON, d_strength & 0xf0fff0ff); + rk628_i2c_write(rk628, GRF_GPIO3B_D_CON, clk_strength | 0x000f0000); + + /* rk628: modify DCLK delay for RGB */ + if (rk628->version == RK628F_VERSION) { + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, + dclk_delay & 0xffffffff); + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, + dclk_delay >> 32); + } } -void rk628_rgb_encoder_enable(struct rk628 *rk628) +static void rk628_rgb_encoder_disable(struct rk628 *rk628) { - rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, - SW_BT_DATA_OEN_MASK | SW_OUTPUT_MODE_MASK, - SW_OUTPUT_MODE(OUTPUT_MODE_RGB)); - rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, SW_DCLK_OUT_INV_EN, - SW_DCLK_OUT_INV_EN); -} - -void rk628_rgb_encoder_disable(struct rk628 *rk628) -{ - rk628_panel_disable(rk628); - rk628_panel_unprepare(rk628); } @@ -66,43 +205,90 @@ void rk628_rgb_tx_enable(struct rk628 *rk628) void rk628_rgb_tx_disable(struct rk628 *rk628) { rk628_panel_disable(rk628); + rk628_panel_unprepare(rk628); + + rk628_rgb_encoder_disable(rk628); } -void rk628_bt1120_decoder_enable(struct rk628 *rk628) +static void rk628_bt1120_decoder_timing_cfg(struct rk628 *rk628) +{ + u32 src_hsync_len, src_hback_porch, src_hfront_porch, src_hactive; + u32 src_vsync_len, src_vback_porch, src_vfront_porch, src_vactive; + u32 dsp_htotal, dsp_hs_end, dsp_hact_st; + u32 dsp_vtotal, dsp_vs_end, dsp_vact_st; + u32 dsp_hbor_st, dsp_vbor_st; + u16 bor_left = 0, bor_up = 0; + struct rk628_display_mode *src = &rk628->src_mode; + + src_hactive = src->hdisplay; + src_hsync_len = src->hsync_end - src->hsync_start; + src_hback_porch = src->htotal - src->hsync_end; + src_hfront_porch = src->hsync_start - src->hdisplay; + src_vsync_len = src->vsync_end - src->vsync_start; + src_vback_porch = src->vtotal - src->vsync_end; + src_vfront_porch = src->vsync_start - src->vdisplay; + src_vactive = src->vdisplay; + + dsp_htotal = src_hsync_len + src_hback_porch + + src_hactive + src_hfront_porch; + dsp_vtotal = src_vsync_len + src_vback_porch + + src_vactive + src_vfront_porch; + dsp_hs_end = src_hsync_len; + dsp_vs_end = src_vsync_len; + dsp_hbor_st = src_hsync_len + src_hback_porch; + dsp_vbor_st = src_vsync_len + src_vback_porch; + dsp_hact_st = dsp_hbor_st + bor_left; + dsp_vact_st = dsp_vbor_st + bor_up; + + if (rk628->version == RK628F_VERSION) + rk628_i2c_update_bits(rk628, GRF_RGB_RX_DBG_MEAS0, + RGB_RX_MODET_EN | RGB_RX_DCLK_EN, + RGB_RX_MODET_EN | RGB_RX_DCLK_EN); + + rk628_i2c_write(rk628, GRF_BT1120_TIMING_CTRL0, BT1120_DSP_HS_END(dsp_hs_end) | + BT1120_DSP_HTOTAL(dsp_htotal)); + rk628_i2c_write(rk628, GRF_BT1120_TIMING_CTRL1, BT1120_DSP_HACT_ST(dsp_hact_st)); + rk628_i2c_write(rk628, GRF_BT1120_TIMING_CTRL2, BT1120_DSP_VS_END(dsp_vs_end) | + BT1120_DSP_VTOTAL(dsp_vtotal)); + rk628_i2c_write(rk628, GRF_BT1120_TIMING_CTRL3, BT1120_DSP_VACT_ST(dsp_vact_st)); +} + +static void rk628_bt1120_decoder_enable(struct rk628 *rk628) { struct rk628_display_mode *mode = rk628_display_get_src_mode(rk628); + rk628_set_input_bus_format(rk628, BUS_FMT_YUV422); + /* pinctrl for vop pin */ rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff); rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555); rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b); - /* rk628: modify IO drive strength for RGB */ - rk628_i2c_write(rk628, GRF_GPIO2A_D0_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2A_D1_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2B_D0_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2B_D1_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2C_D0_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2C_D1_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO3A_D0_CON, 0xffff1011); - rk628_i2c_write(rk628, GRF_GPIO3B_D_CON, 0x10001); - /* config sw_input_mode bt1120 */ rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_INPUT_MODE_MASK, SW_INPUT_MODE(INPUT_MODE_BT1120)); + if (rk628->version == RK628F_VERSION) + rk628_bt1120_decoder_timing_cfg(rk628); + /* operation resetn_bt1120dec */ rk628_i2c_write(rk628, CRU_SOFTRST_CON00, 0x10001000); rk628_i2c_write(rk628, CRU_SOFTRST_CON00, 0x10000000); rk628_cru_clk_set_rate(rk628, CGU_BT1120DEC, mode->clock * 1000); -#ifdef BT1120_DUAL_EDGE - rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON0, - DEC_DUALEDGE_EN, DEC_DUALEDGE_EN); - rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, 0x10000000); - rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0); -#endif + if (rk628->rgb.bt1120_dual_edge) { + rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON0, + DEC_DUALEDGE_EN, DEC_DUALEDGE_EN); + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, 0x10000000); + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0); + } else { + if (rk628->version == RK628F_VERSION) { + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, + 0x10000); + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0); + } + } rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON1, SW_SET_X_MASK, SW_SET_X(mode->hdisplay)); @@ -118,38 +304,82 @@ void rk628_bt1120_decoder_enable(struct rk628 *rk628) SW_CAP_EN_PSYNC | SW_CAP_EN_ASYNC | SW_PROGRESS_EN); } -void rk628_bt1120_encoder_enable(struct rk628 *rk628) +static void rk628_bt1120_encoder_enable(struct rk628 *rk628) { u32 val = 0; + int voltage = 0; + u32 strength; + u64 dclk_delay; + + rk628_set_output_bus_format(rk628, BUS_FMT_YUV422); /* pinctrl for vop pin */ rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff); rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555); rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b); - /* rk628: modify IO drive strength for RGB */ - rk628_i2c_write(rk628, GRF_GPIO2A_D0_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2A_D1_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2B_D0_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2B_D1_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2C_D0_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO2C_D1_CON, 0xffff1111); - rk628_i2c_write(rk628, GRF_GPIO3A_D0_CON, 0xffff1011); - rk628_i2c_write(rk628, GRF_GPIO3B_D_CON, 0x10001); - - /* config sw_input_mode bt1120 */ rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, - SW_BT_DATA_OEN_MASK | SW_OUTPUT_MODE_MASK, - SW_OUTPUT_MODE(OUTPUT_MODE_BT1120)); + SW_BT_DATA_OEN_MASK | SW_OUTPUT_RGB_MODE_MASK, + SW_OUTPUT_RGB_MODE(OUTPUT_MODE_BT1120 >> 3)); rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_R2Y_EN(1)); - rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, - SW_DCLK_OUT_INV_EN, SW_DCLK_OUT_INV_EN); + if (rk628->version != RK628F_VERSION) + rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, + SW_DCLK_OUT_INV_EN, SW_DCLK_OUT_INV_EN); + + /* + * Under the same drive strength and DCLK delay, the signal behaves + * differently under different voltage power domains. In order to + * center the eye diagram of the signal, have sufficient signal setup + * and hold time, and ensure that the signal does not overshoot, the + * drive strength and DCLK delay need to be set for the power domains + * of different voltages. + */ + if (rk628->rgb.vccio_rgb) + voltage = regulator_get_voltage(rk628->rgb.vccio_rgb); + + switch (voltage) { + case 1800000: + strength = 3; + dclk_delay = 0x100000000; + break; + case 3300000: + strength = 1; + dclk_delay = 0x1000000000; + break; + default: + strength = 1; + dclk_delay = 0x1000000000; + } + + /* rk628: modify IO drive strength for BT1120 */ + if (rk628->version == RK628F_VERSION) + strength = strength * 0x1111 | 0xffff0000; + else + strength = 0xffff1111; + + rk628_i2c_write(rk628, GRF_GPIO2A_D0_CON, strength); + rk628_i2c_write(rk628, GRF_GPIO2A_D1_CON, strength); + rk628_i2c_write(rk628, GRF_GPIO2B_D0_CON, strength); + rk628_i2c_write(rk628, GRF_GPIO2B_D1_CON, strength); + rk628_i2c_write(rk628, GRF_GPIO2C_D0_CON, strength); + rk628_i2c_write(rk628, GRF_GPIO2C_D1_CON, strength); + rk628_i2c_write(rk628, GRF_GPIO3A_D0_CON, strength & 0xf0fff0ff); + rk628_i2c_write(rk628, GRF_GPIO3B_D_CON, strength & 0x000f000f); + + /* rk628: modify DCLK delay for BT1120 */ + if (rk628->rgb.bt1120_dual_edge) { + val |= ENC_DUALEDGE_EN(1); + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, 0x10000000); + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0); + } else { + if (rk628->version == RK628F_VERSION) { + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, + dclk_delay & 0xffffffff); + rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, + dclk_delay >> 32); + } + } -#ifdef BT1120_DUAL_EDGE - val |= ENC_DUALEDGE_EN(1); - rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, 0x10000000); - rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0); -#endif val |= BT1120_UV_SWAP(1); rk628_i2c_write(rk628, GRF_RGB_ENC_CON, val); } @@ -163,4 +393,3 @@ void rk628_bt1120_tx_enable(struct rk628 *rk628) { rk628_bt1120_encoder_enable(rk628); } - diff --git a/drivers/misc/rk628/rk628_rgb.h b/drivers/misc/rk628/rk628_rgb.h index e4bd486653b8..fb5c326b3310 100644 --- a/drivers/misc/rk628/rk628_rgb.h +++ b/drivers/misc/rk628/rk628_rgb.h @@ -9,9 +9,11 @@ #define RK628_RGB_H #include "rk628.h" +int rk628_rgb_parse(struct rk628 *rk628, struct device_node *rgb_np); void rk628_rgb_rx_enable(struct rk628 *rk628); void rk628_rgb_tx_enable(struct rk628 *rk628); void rk628_rgb_tx_disable(struct rk628 *rk628); void rk628_bt1120_rx_enable(struct rk628 *rk628); void rk628_bt1120_tx_enable(struct rk628 *rk628); +void rk628_rgb_decoder_create_debugfs_file(struct rk628 *rk628); #endif