From a763c08e05ae400d46863c8aa2d4d2ee303ed533 Mon Sep 17 00:00:00 2001 From: amazingfate Date: Mon, 18 Mar 2024 13:42:52 +0800 Subject: [PATCH] rockchip-rk3588-edge: use hdmi patches from collabora --- ...026-Add-missing-nodes-to-Orange-Pi-5.patch | 40 +- ...dd-missing-nodes-to-Orange-Pi-5-Plus.patch | 49 +- .../0028-Add-HDMI-QP-Support-for-RK3588.patch | 13701 ---------------- .../0029-Add-VOP2-Support-for-RK3588.patch | 833 - .../0031-Add-HDMI-and-VOP2-to-Rock-5A.patch | 33 +- ...dd-Samsung-HDMI-eDP-Combo-PHY-driver.patch | 1091 ++ ...dts-rockchip-Add-HDMI0-PHY-to-rk3588.patch | 54 + ...sys-Add-initial-support-for-DW-HDMI-.patch | 6617 ++++++++ ...-rockchip-Add-HDMI0-bridge-to-rk3588.patch | 81 + ...ip-Enable-HDMI0-PHY-clk-provider-on-.patch | 26 + ...ts-rockchip-Enable-HDMI0-on-rock-5b.patch} | 67 +- .../board-khadas-edge2-add-nodes.patch | 47 +- .../board-khadas-edge2-mcu.patch | 46 +- .../dt/rk3588-hinlink-h88k.dts | 18 - .../dt/rk3588s-nanopi-r6s.dts | 18 - 15 files changed, 7954 insertions(+), 14767 deletions(-) delete mode 100644 patch/kernel/rockchip-rk3588-edge/0028-Add-HDMI-QP-Support-for-RK3588.patch delete mode 100644 patch/kernel/rockchip-rk3588-edge/0029-Add-VOP2-Support-for-RK3588.patch create mode 100644 patch/kernel/rockchip-rk3588-edge/0035-phy-rockchip-Add-Samsung-HDMI-eDP-Combo-PHY-driver.patch create mode 100644 patch/kernel/rockchip-rk3588-edge/0036-arm64-dts-rockchip-Add-HDMI0-PHY-to-rk3588.patch create mode 100644 patch/kernel/rockchip-rk3588-edge/0037-drm-bridge-synopsys-Add-initial-support-for-DW-HDMI-.patch create mode 100644 patch/kernel/rockchip-rk3588-edge/0038-arm64-dts-rockchip-Add-HDMI0-bridge-to-rk3588.patch create mode 100644 patch/kernel/rockchip-rk3588-edge/0039-arm64-dts-rockchip-Enable-HDMI0-PHY-clk-provider-on-.patch rename patch/kernel/rockchip-rk3588-edge/{0032-Add-HDMI-and-VOP2-to-Rock-5B.patch => 0040-arm64-dts-rockchip-Enable-HDMI0-on-rock-5b.patch} (53%) diff --git a/patch/kernel/rockchip-rk3588-edge/0026-Add-missing-nodes-to-Orange-Pi-5.patch b/patch/kernel/rockchip-rk3588-edge/0026-Add-missing-nodes-to-Orange-Pi-5.patch index b0dcd38928..3dcb58b5a3 100644 --- a/patch/kernel/rockchip-rk3588-edge/0026-Add-missing-nodes-to-Orange-Pi-5.patch +++ b/patch/kernel/rockchip-rk3588-edge/0026-Add-missing-nodes-to-Orange-Pi-5.patch @@ -4,11 +4,11 @@ Date: Thu, 16 Nov 2023 18:09:07 +0300 Subject: arm64: dts: Add missing nodes to Orange Pi 5 --- - arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts | 215 +++++++++- - 1 file changed, 214 insertions(+), 1 deletion(-) + arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts | 197 +++++++++- + 1 file changed, 196 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts b/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts -index 25de4362af38..f5e86d99084d 100644 +index 25de4362af38..42ce897faf0f 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts @@ -6,6 +6,8 @@ @@ -20,7 +20,7 @@ index 25de4362af38..f5e86d99084d 100644 #include "rk3588s.dtsi" / { -@@ -47,6 +49,51 @@ led-1 { +@@ -47,6 +49,40 @@ led-1 { }; }; @@ -57,22 +57,11 @@ index 25de4362af38..f5e86d99084d 100644 + system-clock-frequency = <12288000>; + }; + }; -+ -+ hdmi-con { -+ compatible = "hdmi-connector"; -+ type = "a"; -+ -+ port { -+ hdmi_con_in: endpoint { -+ remote-endpoint = <&hdmi0_out_con>; -+ }; -+ }; -+ }; + vbus_typec: vbus-typec-regulator { compatible = "regulator-fixed"; enable-active-high; -@@ -102,34 +149,42 @@ &combphy2_psu { +@@ -102,34 +138,42 @@ &combphy2_psu { &cpu_b0 { cpu-supply = <&vdd_cpu_big0_s0>; @@ -115,7 +104,7 @@ index 25de4362af38..f5e86d99084d 100644 }; &gmac1 { -@@ -223,6 +278,75 @@ hym8563: rtc@51 { +@@ -223,6 +267,75 @@ hym8563: rtc@51 { interrupts = ; wakeup-source; }; @@ -191,7 +180,7 @@ index 25de4362af38..f5e86d99084d 100644 }; &mdio1 { -@@ -263,6 +387,12 @@ typec5v_pwren: typec5v-pwren { +@@ -263,6 +376,12 @@ typec5v_pwren: typec5v-pwren { rockchip,pins = <3 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>; }; }; @@ -204,7 +193,7 @@ index 25de4362af38..f5e86d99084d 100644 }; &saradc { -@@ -364,7 +494,7 @@ regulator-state-mem { +@@ -364,7 +483,7 @@ regulator-state-mem { }; }; @@ -213,7 +202,7 @@ index 25de4362af38..f5e86d99084d 100644 regulator-name = "vdd_cpu_lit_s0"; regulator-always-on; regulator-boot-on; -@@ -625,6 +755,14 @@ &tsadc { +@@ -625,6 +744,14 @@ &tsadc { status = "okay"; }; @@ -228,7 +217,7 @@ index 25de4362af38..f5e86d99084d 100644 &u2phy2 { status = "okay"; }; -@@ -650,10 +788,48 @@ &usb_host0_ehci { +@@ -650,10 +777,48 @@ &usb_host0_ehci { status = "okay"; }; @@ -277,13 +266,12 @@ index 25de4362af38..f5e86d99084d 100644 &usb_host1_ehci { status = "okay"; }; -@@ -665,3 +841,40 @@ &usb_host1_ohci { +@@ -665,3 +830,33 @@ &usb_host1_ohci { &usb_host2_xhci { status = "okay"; }; + +&hdmi0 { -+ enable-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + @@ -302,12 +290,6 @@ index 25de4362af38..f5e86d99084d 100644 + }; +}; + -+&hdmi0_out { -+ hdmi0_out_con: endpoint { -+ remote-endpoint = <&hdmi_con_in>; -+ }; -+}; -+ +&vop { + status = "okay"; +}; diff --git a/patch/kernel/rockchip-rk3588-edge/0027-arm64-dts-Add-missing-nodes-to-Orange-Pi-5-Plus.patch b/patch/kernel/rockchip-rk3588-edge/0027-arm64-dts-Add-missing-nodes-to-Orange-Pi-5-Plus.patch index f5cfd4b013..09efd2db4b 100644 --- a/patch/kernel/rockchip-rk3588-edge/0027-arm64-dts-Add-missing-nodes-to-Orange-Pi-5-Plus.patch +++ b/patch/kernel/rockchip-rk3588-edge/0027-arm64-dts-Add-missing-nodes-to-Orange-Pi-5-Plus.patch @@ -4,11 +4,11 @@ Date: Thu, 16 Nov 2023 18:15:09 +0300 Subject: arm64: dts: Add missing nodes to Orange Pi 5 Plus --- - arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts | 190 +++++++++- - 1 file changed, 189 insertions(+), 1 deletion(-) + arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts | 172 +++++++++- + 1 file changed, 171 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts b/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts -index 3e660ff6cd5f..ef7f9386fc02 100644 +index 3e660ff6cd5f..ee53ed120909 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts @@ -10,6 +10,7 @@ @@ -19,25 +19,7 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 #include "rk3588.dtsi" / { -@@ -158,6 +159,17 @@ daicodec: simple-audio-card,codec { - }; - }; - -+ hdmi-con { -+ compatible = "hdmi-connector"; -+ type = "a"; -+ -+ port { -+ hdmi_con_in: endpoint { -+ remote-endpoint = <&hdmi0_out_con>; -+ }; -+ }; -+ }; -+ - vcc3v3_pcie30: vcc3v3-pcie30-regulator { - compatible = "regulator-fixed"; - enable-active-high; -@@ -199,6 +211,18 @@ vcc5v0_sys: vcc5v0-sys-regulator { +@@ -199,6 +200,18 @@ vcc5v0_sys: vcc5v0-sys-regulator { regulator-max-microvolt = <5000000>; }; @@ -56,7 +38,7 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 vcc5v0_usb20: vcc5v0-usb20-regulator { compatible = "regulator-fixed"; enable-active-high; -@@ -226,34 +250,42 @@ &combphy2_psu { +@@ -226,34 +239,42 @@ &combphy2_psu { &cpu_b0 { cpu-supply = <&vdd_cpu_big0_s0>; @@ -99,7 +81,7 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 }; &i2c0 { -@@ -311,6 +343,53 @@ hym8563: rtc@51 { +@@ -311,6 +332,53 @@ hym8563: rtc@51 { pinctrl-0 = <&hym8563_int>; wakeup-source; }; @@ -153,7 +135,7 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 }; &i2c7 { -@@ -408,6 +487,14 @@ hp_detect: hp-detect { +@@ -408,6 +476,14 @@ hp_detect: hp-detect { }; usb { @@ -168,7 +150,7 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 vcc5v0_usb20_en: vcc5v0-usb20-en { rockchip,pins = <3 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; }; -@@ -535,7 +622,7 @@ regulator-state-mem { +@@ -535,7 +611,7 @@ regulator-state-mem { }; }; @@ -177,7 +159,7 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 regulator-name = "vdd_cpu_lit_s0"; regulator-always-on; regulator-boot-on; -@@ -802,6 +889,22 @@ &tsadc { +@@ -802,6 +878,22 @@ &tsadc { status = "okay"; }; @@ -200,7 +182,7 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 &u2phy2 { status = "okay"; }; -@@ -830,6 +933,35 @@ &uart9 { +@@ -830,6 +922,35 @@ &uart9 { status = "okay"; }; @@ -236,7 +218,7 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 &usb_host0_ehci { status = "okay"; }; -@@ -838,6 +970,20 @@ &usb_host0_ohci { +@@ -838,6 +959,20 @@ &usb_host0_ohci { status = "okay"; }; @@ -257,7 +239,7 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 &usb_host1_ehci { status = "okay"; }; -@@ -845,3 +991,45 @@ &usb_host1_ehci { +@@ -845,3 +980,38 @@ &usb_host1_ehci { &usb_host1_ohci { status = "okay"; }; @@ -268,7 +250,6 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 +}; + +&hdmi0 { -+ enable-gpios = <&gpio4 RK_PB1 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + @@ -287,12 +268,6 @@ index 3e660ff6cd5f..ef7f9386fc02 100644 + }; +}; + -+&hdmi0_out { -+ hdmi0_out_con: endpoint { -+ remote-endpoint = <&hdmi_con_in>; -+ }; -+}; -+ +&vop { + status = "okay"; +}; diff --git a/patch/kernel/rockchip-rk3588-edge/0028-Add-HDMI-QP-Support-for-RK3588.patch b/patch/kernel/rockchip-rk3588-edge/0028-Add-HDMI-QP-Support-for-RK3588.patch deleted file mode 100644 index 12b3a3770e..0000000000 --- a/patch/kernel/rockchip-rk3588-edge/0028-Add-HDMI-QP-Support-for-RK3588.patch +++ /dev/null @@ -1,13701 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Algea Cao -Date: Tue, 12 Jul 2022 09:18:25 +0200 -Subject: phy/rockchip: Add the hdptx-hdmi driver - -This is a phy driver for rk3588 hdmi. - -Signed-off-by: Algea Cao -Signed-off-by: Andy Yan ---- - drivers/phy/rockchip/Kconfig | 7 + - drivers/phy/rockchip/Makefile | 1 + - drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c | 2347 ++++++++++ - 3 files changed, 2355 insertions(+) - -diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig -index d21b458c1d18..62c18e25b8e0 100644 ---- a/drivers/phy/rockchip/Kconfig -+++ b/drivers/phy/rockchip/Kconfig -@@ -83,6 +83,13 @@ config PHY_ROCKCHIP_PCIE - help - Enable this to support the Rockchip PCIe PHY. - -+config PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI -+ tristate "Rockchip Samsung HDMI/DP Combo PHY HDMI driver" -+ depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) -+ select GENERIC_PHY -+ help -+ Support for Rockchip HDMI/DP Combo PHY with Samsung IP block. -+ - config PHY_ROCKCHIP_SNPS_PCIE3 - tristate "Rockchip Snps PCIe3 PHY Driver" - depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST -diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile -index 25d2e1355db7..d266414a1ffb 100644 ---- a/drivers/phy/rockchip/Makefile -+++ b/drivers/phy/rockchip/Makefile -@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o - obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o - obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o - obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o -+obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI) += phy-rockchip-samsung-hdptx-hdmi.o - obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o - obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o - obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o -diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c -new file mode 100644 -index 000000000000..036db08777cf ---- /dev/null -+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c -@@ -0,0 +1,2347 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright (C) Rockchip Electronics Co.Ltd -+ * Author: -+ * Algea Cao -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l))) -+ -+#define GRF_HDPTX_CON0 0x00 -+#define HDPTX_I_PLL_EN BIT(7) -+#define HDPTX_I_BIAS_EN BIT(6) -+#define HDPTX_I_BGR_EN BIT(5) -+#define GRF_HDPTX_STATUS 0x80 -+#define HDPTX_O_PLL_LOCK_DONE BIT(3) -+#define HDPTX_O_PHY_CLK_RDY BIT(2) -+#define HDPTX_O_PHY_RDY BIT(1) -+#define HDPTX_O_SB_RDY BIT(0) -+ -+#define CMN_REG0000 0x0000 -+#define CMN_REG0001 0x0004 -+#define CMN_REG0002 0x0008 -+#define CMN_REG0003 0x000C -+#define CMN_REG0004 0x0010 -+#define CMN_REG0005 0x0014 -+#define CMN_REG0006 0x0018 -+#define CMN_REG0007 0x001C -+#define CMN_REG0008 0x0020 -+#define LCPLL_EN_MASK BIT(6) -+#define LCPLL_EN(x) UPDATE(x, 4, 4) -+#define LCPLL_LCVCO_MODE_EN_MASK BIT(4) -+#define LCPLL_LCVCO_MODE_EN(x) UPDATE(x, 4, 4) -+#define CMN_REG0009 0x0024 -+#define CMN_REG000A 0x0028 -+#define CMN_REG000B 0x002C -+#define CMN_REG000C 0x0030 -+#define CMN_REG000D 0x0034 -+#define CMN_REG000E 0x0038 -+#define CMN_REG000F 0x003C -+#define CMN_REG0010 0x0040 -+#define CMN_REG0011 0x0044 -+#define CMN_REG0012 0x0048 -+#define CMN_REG0013 0x004C -+#define CMN_REG0014 0x0050 -+#define CMN_REG0015 0x0054 -+#define CMN_REG0016 0x0058 -+#define CMN_REG0017 0x005C -+#define CMN_REG0018 0x0060 -+#define CMN_REG0019 0x0064 -+#define CMN_REG001A 0x0068 -+#define CMN_REG001B 0x006C -+#define CMN_REG001C 0x0070 -+#define CMN_REG001D 0x0074 -+#define CMN_REG001E 0x0078 -+#define LCPLL_PI_EN_MASK BIT(5) -+#define LCPLL_PI_EN(x) UPDATE(x, 5, 5) -+#define LCPLL_100M_CLK_EN_MASK BIT(0) -+#define LCPLL_100M_CLK_EN(x) UPDATE(x, 0, 0) -+#define CMN_REG001F 0x007C -+#define CMN_REG0020 0x0080 -+#define CMN_REG0021 0x0084 -+#define CMN_REG0022 0x0088 -+#define CMN_REG0023 0x008C -+#define CMN_REG0024 0x0090 -+#define CMN_REG0025 0x0094 -+#define LCPLL_PMS_IQDIV_RSTN BIT(4) -+#define CMN_REG0026 0x0098 -+#define CMN_REG0027 0x009C -+#define CMN_REG0028 0x00A0 -+#define LCPLL_SDC_FRAC_EN BIT(2) -+#define LCPLL_SDC_FRAC_RSTN BIT(0) -+#define CMN_REG0029 0x00A4 -+#define CMN_REG002A 0x00A8 -+#define CMN_REG002B 0x00AC -+#define CMN_REG002C 0x00B0 -+#define CMN_REG002D 0x00B4 -+#define LCPLL_SDC_N_MASK GENMASK(3, 1) -+#define LCPLL_SDC_N(x) UPDATE(x, 3, 1) -+#define CMN_REG002E 0x00B8 -+#define LCPLL_SDC_NUMBERATOR_MASK GENMASK(5, 0) -+#define LCPLL_SDC_NUMBERATOR(x) UPDATE(x, 5, 0) -+#define CMN_REG002F 0x00BC -+#define LCPLL_SDC_DENOMINATOR_MASK GENMASK(7, 2) -+#define LCPLL_SDC_DENOMINATOR(x) UPDATE(x, 7, 2) -+#define LCPLL_SDC_NDIV_RSTN BIT(0) -+#define CMN_REG0030 0x00C0 -+#define CMN_REG0031 0x00C4 -+#define CMN_REG0032 0x00C8 -+#define CMN_REG0033 0x00CC -+#define CMN_REG0034 0x00D0 -+#define CMN_REG0035 0x00D4 -+#define CMN_REG0036 0x00D8 -+#define CMN_REG0037 0x00DC -+#define CMN_REG0038 0x00E0 -+#define CMN_REG0039 0x00E4 -+#define CMN_REG003A 0x00E8 -+#define CMN_REG003B 0x00EC -+#define CMN_REG003C 0x00F0 -+#define CMN_REG003D 0x00F4 -+#define ROPLL_LCVCO_EN BIT(4) -+#define CMN_REG003E 0x00F8 -+#define CMN_REG003F 0x00FC -+#define CMN_REG0040 0x0100 -+#define CMN_REG0041 0x0104 -+#define CMN_REG0042 0x0108 -+#define CMN_REG0043 0x010C -+#define CMN_REG0044 0x0110 -+#define CMN_REG0045 0x0114 -+#define CMN_REG0046 0x0118 -+#define CMN_REG0047 0x011C -+#define CMN_REG0048 0x0120 -+#define CMN_REG0049 0x0124 -+#define CMN_REG004A 0x0128 -+#define CMN_REG004B 0x012C -+#define CMN_REG004C 0x0130 -+#define CMN_REG004D 0x0134 -+#define CMN_REG004E 0x0138 -+#define ROPLL_PI_EN BIT(5) -+#define CMN_REG004F 0x013C -+#define CMN_REG0050 0x0140 -+#define CMN_REG0051 0x0144 -+#define CMN_REG0052 0x0148 -+#define CMN_REG0053 0x014C -+#define CMN_REG0054 0x0150 -+#define CMN_REG0055 0x0154 -+#define CMN_REG0056 0x0158 -+#define CMN_REG0057 0x015C -+#define CMN_REG0058 0x0160 -+#define CMN_REG0059 0x0164 -+#define CMN_REG005A 0x0168 -+#define CMN_REG005B 0x016C -+#define CMN_REG005C 0x0170 -+#define ROPLL_PMS_IQDIV_RSTN BIT(5) -+#define CMN_REG005D 0x0174 -+#define CMN_REG005E 0x0178 -+#define ROPLL_SDM_EN_MASK BIT(6) -+#define ROPLL_SDM_EN(x) UPDATE(x, 6, 6) -+#define ROPLL_SDM_FRAC_EN_RBR BIT(3) -+#define ROPLL_SDM_FRAC_EN_HBR BIT(2) -+#define ROPLL_SDM_FRAC_EN_HBR2 BIT(1) -+#define ROPLL_SDM_FRAC_EN_HBR3 BIT(0) -+#define CMN_REG005F 0x017C -+#define CMN_REG0060 0x0180 -+#define CMN_REG0061 0x0184 -+#define CMN_REG0062 0x0188 -+#define CMN_REG0063 0x018C -+#define CMN_REG0064 0x0190 -+#define ROPLL_SDM_NUM_SIGN_RBR_MASK BIT(3) -+#define ROPLL_SDM_NUM_SIGN_RBR(x) UPDATE(x, 3, 3) -+#define CMN_REG0065 0x0194 -+#define CMN_REG0066 0x0198 -+#define CMN_REG0067 0x019C -+#define CMN_REG0068 0x01A0 -+#define CMN_REG0069 0x01A4 -+#define ROPLL_SDC_N_RBR_MASK GENMASK(2, 0) -+#define ROPLL_SDC_N_RBR(x) UPDATE(x, 2, 0) -+#define CMN_REG006A 0x01A8 -+#define CMN_REG006B 0x01AC -+#define CMN_REG006C 0x01B0 -+#define CMN_REG006D 0x01B4 -+#define CMN_REG006E 0x01B8 -+#define CMN_REG006F 0x01BC -+#define CMN_REG0070 0x01C0 -+#define CMN_REG0071 0x01C4 -+#define CMN_REG0072 0x01C8 -+#define CMN_REG0073 0x01CC -+#define CMN_REG0074 0x01D0 -+#define ROPLL_SDC_NDIV_RSTN BIT(2) -+#define ROPLL_SSC_EN BIT(0) -+#define CMN_REG0075 0x01D4 -+#define CMN_REG0076 0x01D8 -+#define CMN_REG0077 0x01DC -+#define CMN_REG0078 0x01E0 -+#define CMN_REG0079 0x01E4 -+#define CMN_REG007A 0x01E8 -+#define CMN_REG007B 0x01EC -+#define CMN_REG007C 0x01F0 -+#define CMN_REG007D 0x01F4 -+#define CMN_REG007E 0x01F8 -+#define CMN_REG007F 0x01FC -+#define CMN_REG0080 0x0200 -+#define CMN_REG0081 0x0204 -+#define OVRD_PLL_CD_CLK_EN BIT(8) -+#define PLL_CD_HSCLK_EAST_EN BIT(0) -+#define CMN_REG0082 0x0208 -+#define CMN_REG0083 0x020C -+#define CMN_REG0084 0x0210 -+#define CMN_REG0085 0x0214 -+#define CMN_REG0086 0x0218 -+#define PLL_PCG_POSTDIV_SEL_MASK GENMASK(7, 4) -+#define PLL_PCG_POSTDIV_SEL(x) UPDATE(x, 7, 4) -+#define PLL_PCG_CLK_SEL_MASK GENMASK(3, 1) -+#define PLL_PCG_CLK_SEL(x) UPDATE(x, 3, 1) -+#define PLL_PCG_CLK_EN BIT(0) -+#define CMN_REG0087 0x021C -+#define PLL_FRL_MODE_EN BIT(3) -+#define PLL_TX_HS_CLK_EN BIT(2) -+#define CMN_REG0088 0x0220 -+#define CMN_REG0089 0x0224 -+#define LCPLL_ALONE_MODE BIT(1) -+#define CMN_REG008A 0x0228 -+#define CMN_REG008B 0x022C -+#define CMN_REG008C 0x0230 -+#define CMN_REG008D 0x0234 -+#define CMN_REG008E 0x0238 -+#define CMN_REG008F 0x023C -+#define CMN_REG0090 0x0240 -+#define CMN_REG0091 0x0244 -+#define CMN_REG0092 0x0248 -+#define CMN_REG0093 0x024C -+#define CMN_REG0094 0x0250 -+#define CMN_REG0095 0x0254 -+#define CMN_REG0096 0x0258 -+#define CMN_REG0097 0x025C -+#define DIG_CLK_SEL BIT(1) -+#define ROPLL_REF BIT(1) -+#define LCPLL_REF 0 -+#define CMN_REG0098 0x0260 -+#define CMN_REG0099 0x0264 -+#define CMN_ROPLL_ALONE_MODE BIT(2) -+#define ROPLL_ALONE_MODE BIT(2) -+#define CMN_REG009A 0x0268 -+#define HS_SPEED_SEL BIT(0) -+#define DIV_10_CLOCK BIT(0) -+#define CMN_REG009B 0x026C -+#define IS_SPEED_SEL BIT(4) -+#define LINK_SYMBOL_CLOCK BIT(4) -+#define LINK_SYMBOL_CLOCK1_2 0 -+#define CMN_REG009C 0x0270 -+#define CMN_REG009D 0x0274 -+#define CMN_REG009E 0x0278 -+#define CMN_REG009F 0x027C -+#define CMN_REG00A0 0x0280 -+#define CMN_REG00A1 0x0284 -+#define CMN_REG00A2 0x0288 -+#define CMN_REG00A3 0x028C -+#define CMN_REG00AD 0x0290 -+#define CMN_REG00A5 0x0294 -+#define CMN_REG00A6 0x0298 -+#define CMN_REG00A7 0x029C -+#define SB_REG0100 0x0400 -+#define SB_REG0101 0x0404 -+#define SB_REG0102 0x0408 -+#define OVRD_SB_RXTERM_EN_MASK BIT(5) -+#define OVRD_SB_RXTERM_EN(x) UPDATE(x, 5, 5) -+#define SB_RXTERM_EN_MASK BIT(4) -+#define SB_RXTERM_EN(x) UPDATE(x, 4, 4) -+#define ANA_SB_RXTERM_OFFSP_MASK GENMASK(3, 0) -+#define ANA_SB_RXTERM_OFFSP(x) UPDATE(x, 3, 0) -+#define SB_REG0103 0x040C -+#define ANA_SB_RXTERM_OFFSN_MASK GENMASK(6, 3) -+#define ANA_SB_RXTERM_OFFSN(x) UPDATE(x, 6, 3) -+#define OVRD_SB_RX_RESCAL_DONE_MASK BIT(1) -+#define OVRD_SB_RX_RESCAL_DONE(x) UPDATE(x, 1, 1) -+#define SB_RX_RESCAL_DONE_MASK BIT(0) -+#define SB_RX_RESCAL_DONE(x) UPDATE(x, 0, 0) -+#define SB_REG0104 0x0410 -+#define OVRD_SB_EN_MASK BIT(5) -+#define OVRD_SB_EN(x) UPDATE(x, 5, 5) -+#define SB_EN_MASK BIT(4) -+#define SB_EN(x) UPDATE(x, 4, 4) -+#define SB_REG0105 0x0414 -+#define OVRD_SB_EARC_CMDC_EN_MASK BIT(6) -+#define OVRD_SB_EARC_CMDC_EN(x) UPDATE(x, 6, 6) -+#define SB_EARC_CMDC_EN_MASK BIT(5) -+#define SB_EARC_CMDC_EN(x) UPDATE(x, 5, 5) -+#define ANA_SB_TX_HLVL_PROG_MASK GENMASK(2, 0) -+#define ANA_SB_TX_HLVL_PROG(x) UPDATE(x, 2, 0) -+#define SB_REG0106 0x0418 -+#define ANA_SB_TX_LLVL_PROG_MASK GENMASK(6, 4) -+#define ANA_SB_TX_LLVL_PROG(x) UPDATE(x, 6, 4) -+#define SB_REG0107 0x041C -+#define SB_REG0108 0x0420 -+#define SB_REG0109 0x0424 -+#define ANA_SB_DMRX_AFC_DIV_RATIO_MASK GENMASK(2, 0) -+#define ANA_SB_DMRX_AFC_DIV_RATIO(x) UPDATE(x, 2, 0) -+#define SB_REG010A 0x0428 -+#define SB_REG010B 0x042C -+#define SB_REG010C 0x0430 -+#define SB_REG010D 0x0434 -+#define SB_REG010E 0x0438 -+#define SB_REG010F 0x043C -+#define OVRD_SB_VREG_EN_MASK BIT(7) -+#define OVRD_SB_VREG_EN(x) UPDATE(x, 7, 7) -+#define SB_VREG_EN_MASK BIT(6) -+#define SB_VREG_EN(x) UPDATE(x, 6, 6) -+#define OVRD_SB_VREG_LPF_BYPASS_MASK BIT(5) -+#define OVRD_SB_VREG_LPF_BYPASS(x) UPDATE(x, 5, 5) -+#define SB_VREG_LPF_BYPASS_MASK BIT(4) -+#define SB_VREG_LPF_BYPASS(x) UPDATE(x, 4, 4) -+#define ANA_SB_VREG_GAIN_CTRL_MASK GENMASK(3, 0) -+#define ANA_SB_VREG_GAIN_CTRL(x) UPDATE(x, 3, 0) -+#define SB_REG0110 0x0440 -+#define ANA_SB_VREG_REF_SEL_MASK BIT(0) -+#define ANA_SB_VREG_REF_SEL(x) UPDATE(x, 0, 0) -+#define SB_REG0111 0x0444 -+#define SB_REG0112 0x0448 -+#define SB_REG0113 0x044C -+#define SB_RX_RCAL_OPT_CODE_MASK GENMASK(5, 4) -+#define SB_RX_RCAL_OPT_CODE(x) UPDATE(x, 5, 4) -+#define SB_RX_RTERM_CTRL_MASK GENMASK(3, 0) -+#define SB_RX_RTERM_CTRL(x) UPDATE(x, 3, 0) -+#define SB_REG0114 0x0450 -+#define SB_TG_SB_EN_DELAY_TIME_MASK GENMASK(5, 3) -+#define SB_TG_SB_EN_DELAY_TIME(x) UPDATE(x, 5, 3) -+#define SB_TG_RXTERM_EN_DELAY_TIME_MASK GENMASK(2, 0) -+#define SB_TG_RXTERM_EN_DELAY_TIME(x) UPDATE(x, 2, 0) -+#define SB_REG0115 0x0454 -+#define SB_READY_DELAY_TIME_MASK GENMASK(5, 3) -+#define SB_READY_DELAY_TIME(x) UPDATE(x, 5, 3) -+#define SB_TG_OSC_EN_DELAY_TIME_MASK GENMASK(2, 0) -+#define SB_TG_OSC_EN_DELAY_TIME(x) UPDATE(x, 2, 0) -+#define SB_REG0116 0x0458 -+#define AFC_RSTN_DELAY_TIME_MASK GENMASK(6, 4) -+#define AFC_RSTN_DELAY_TIME(x) UPDATE(x, 6, 4) -+#define SB_REG0117 0x045C -+#define FAST_PULSE_TIME_MASK GENMASK(3, 0) -+#define FAST_PULSE_TIME(x) UPDATE(x, 3, 0) -+#define SB_REG0118 0x0460 -+#define SB_REG0119 0x0464 -+#define SB_REG011A 0x0468 -+#define SB_REG011B 0x046C -+#define SB_EARC_SIG_DET_BYPASS_MASK BIT(4) -+#define SB_EARC_SIG_DET_BYPASS(x) UPDATE(x, 4, 4) -+#define SB_AFC_TOL_MASK GENMASK(3, 0) -+#define SB_AFC_TOL(x) UPDATE(x, 3, 0) -+#define SB_REG011C 0x0470 -+#define SB_REG011D 0x0474 -+#define SB_REG011E 0x0478 -+#define SB_REG011F 0x047C -+#define SB_PWM_AFC_CTRL_MASK GENMASK(7, 2) -+#define SB_PWM_AFC_CTRL(x) UPDATE(x, 7, 2) -+#define SB_RCAL_RSTN_MASK BIT(1) -+#define SB_RCAL_RSTN(x) UPDATE(x, 1, 1) -+#define SB_REG0120 0x0480 -+#define SB_EARC_EN_MASK BIT(1) -+#define SB_EARC_EN(x) UPDATE(x, 1, 1) -+#define SB_EARC_AFC_EN_MASK BIT(2) -+#define SB_EARC_AFC_EN(x) UPDATE(x, 2, 2) -+#define SB_REG0121 0x0484 -+#define SB_REG0122 0x0488 -+#define SB_REG0123 0x048C -+#define OVRD_SB_READY_MASK BIT(5) -+#define OVRD_SB_READY(x) UPDATE(x, 5, 5) -+#define SB_READY_MASK BIT(4) -+#define SB_READY(x) UPDATE(x, 4, 4) -+#define SB_REG0124 0x0490 -+#define SB_REG0125 0x0494 -+#define SB_REG0126 0x0498 -+#define SB_REG0127 0x049C -+#define SB_REG0128 0x04A0 -+#define SB_REG0129 0x04AD -+#define LNTOP_REG0200 0x0800 -+#define PROTOCOL_SEL BIT(2) -+#define HDMI_MODE BIT(2) -+#define HDMI_TMDS_FRL_SEL BIT(1) -+#define LNTOP_REG0201 0x0804 -+#define LNTOP_REG0202 0x0808 -+#define LNTOP_REG0203 0x080C -+#define LNTOP_REG0204 0x0810 -+#define LNTOP_REG0205 0x0814 -+#define LNTOP_REG0206 0x0818 -+#define DATA_BUS_WIDTH (0x3 << 1) -+#define WIDTH_40BIT (0x3 << 1) -+#define WIDTH_36BIT (0x2 << 1) -+#define DATA_BUS_SEL BIT(0) -+#define DATA_BUS_36_40 BIT(0) -+#define LNTOP_REG0207 0x081C -+#define LANE_EN 0xf -+#define ALL_LANE_EN 0xf -+#define LNTOP_REG0208 0x0820 -+#define LNTOP_REG0209 0x0824 -+#define LNTOP_REG020A 0x0828 -+#define LNTOP_REG020B 0x082C -+#define LNTOP_REG020C 0x0830 -+#define LNTOP_REG020D 0x0834 -+#define LNTOP_REG020E 0x0838 -+#define LNTOP_REG020F 0x083C -+#define LNTOP_REG0210 0x0840 -+#define LNTOP_REG0211 0x0844 -+#define LNTOP_REG0212 0x0848 -+#define LNTOP_REG0213 0x084C -+#define LNTOP_REG0214 0x0850 -+#define LNTOP_REG0215 0x0854 -+#define LNTOP_REG0216 0x0858 -+#define LNTOP_REG0217 0x085C -+#define LNTOP_REG0218 0x0860 -+#define LNTOP_REG0219 0x0864 -+#define LNTOP_REG021A 0x0868 -+#define LNTOP_REG021B 0x086C -+#define LNTOP_REG021C 0x0870 -+#define LNTOP_REG021D 0x0874 -+#define LNTOP_REG021E 0x0878 -+#define LNTOP_REG021F 0x087C -+#define LNTOP_REG0220 0x0880 -+#define LNTOP_REG0221 0x0884 -+#define LNTOP_REG0222 0x0888 -+#define LNTOP_REG0223 0x088C -+#define LNTOP_REG0224 0x0890 -+#define LNTOP_REG0225 0x0894 -+#define LNTOP_REG0226 0x0898 -+#define LNTOP_REG0227 0x089C -+#define LNTOP_REG0228 0x08A0 -+#define LNTOP_REG0229 0x08A4 -+#define LANE_REG0300 0x0C00 -+#define LANE_REG0301 0x0C04 -+#define LANE_REG0302 0x0C08 -+#define LANE_REG0303 0x0C0C -+#define LANE_REG0304 0x0C10 -+#define LANE_REG0305 0x0C14 -+#define LANE_REG0306 0x0C18 -+#define LANE_REG0307 0x0C1C -+#define LANE_REG0308 0x0C20 -+#define LANE_REG0309 0x0C24 -+#define LANE_REG030A 0x0C28 -+#define LANE_REG030B 0x0C2C -+#define LANE_REG030C 0x0C30 -+#define LANE_REG030D 0x0C34 -+#define LANE_REG030E 0x0C38 -+#define LANE_REG030F 0x0C3C -+#define LANE_REG0310 0x0C40 -+#define LANE_REG0311 0x0C44 -+#define LANE_REG0312 0x0C48 -+#define LN0_TX_SER_RATE_SEL_RBR BIT(5) -+#define LN0_TX_SER_RATE_SEL_HBR BIT(4) -+#define LN0_TX_SER_RATE_SEL_HBR2 BIT(3) -+#define LN0_TX_SER_RATE_SEL_HBR3 BIT(2) -+#define LANE_REG0313 0x0C4C -+#define LANE_REG0314 0x0C50 -+#define LANE_REG0315 0x0C54 -+#define LANE_REG0316 0x0C58 -+#define LANE_REG0317 0x0C5C -+#define LANE_REG0318 0x0C60 -+#define LANE_REG0319 0x0C64 -+#define LANE_REG031A 0x0C68 -+#define LANE_REG031B 0x0C6C -+#define LANE_REG031C 0x0C70 -+#define LANE_REG031D 0x0C74 -+#define LANE_REG031E 0x0C78 -+#define LANE_REG031F 0x0C7C -+#define LANE_REG0320 0x0C80 -+#define LANE_REG0321 0x0C84 -+#define LANE_REG0322 0x0C88 -+#define LANE_REG0323 0x0C8C -+#define LANE_REG0324 0x0C90 -+#define LANE_REG0325 0x0C94 -+#define LANE_REG0326 0x0C98 -+#define LANE_REG0327 0x0C9C -+#define LANE_REG0328 0x0CA0 -+#define LANE_REG0329 0x0CA4 -+#define LANE_REG032A 0x0CA8 -+#define LANE_REG032B 0x0CAC -+#define LANE_REG032C 0x0CB0 -+#define LANE_REG032D 0x0CB4 -+#define LANE_REG0400 0x1000 -+#define LANE_REG0401 0x1004 -+#define LANE_REG0402 0x1008 -+#define LANE_REG0403 0x100C -+#define LANE_REG0404 0x1010 -+#define LANE_REG0405 0x1014 -+#define LANE_REG0406 0x1018 -+#define LANE_REG0407 0x101C -+#define LANE_REG0408 0x1020 -+#define LANE_REG0409 0x1024 -+#define LANE_REG040A 0x1028 -+#define LANE_REG040B 0x102C -+#define LANE_REG040C 0x1030 -+#define LANE_REG040D 0x1034 -+#define LANE_REG040E 0x1038 -+#define LANE_REG040F 0x103C -+#define LANE_REG0410 0x1040 -+#define LANE_REG0411 0x1044 -+#define LANE_REG0412 0x1048 -+#define LN1_TX_SER_RATE_SEL_RBR BIT(5) -+#define LN1_TX_SER_RATE_SEL_HBR BIT(4) -+#define LN1_TX_SER_RATE_SEL_HBR2 BIT(3) -+#define LN1_TX_SER_RATE_SEL_HBR3 BIT(2) -+#define LANE_REG0413 0x104C -+#define LANE_REG0414 0x1050 -+#define LANE_REG0415 0x1054 -+#define LANE_REG0416 0x1058 -+#define LANE_REG0417 0x105C -+#define LANE_REG0418 0x1060 -+#define LANE_REG0419 0x1064 -+#define LANE_REG041A 0x1068 -+#define LANE_REG041B 0x106C -+#define LANE_REG041C 0x1070 -+#define LANE_REG041D 0x1074 -+#define LANE_REG041E 0x1078 -+#define LANE_REG041F 0x107C -+#define LANE_REG0420 0x1080 -+#define LANE_REG0421 0x1084 -+#define LANE_REG0422 0x1088 -+#define LANE_REG0423 0x108C -+#define LANE_REG0424 0x1090 -+#define LANE_REG0425 0x1094 -+#define LANE_REG0426 0x1098 -+#define LANE_REG0427 0x109C -+#define LANE_REG0428 0x10A0 -+#define LANE_REG0429 0x10A4 -+#define LANE_REG042A 0x10A8 -+#define LANE_REG042B 0x10AC -+#define LANE_REG042C 0x10B0 -+#define LANE_REG042D 0x10B4 -+#define LANE_REG0500 0x1400 -+#define LANE_REG0501 0x1404 -+#define LANE_REG0502 0x1408 -+#define LANE_REG0503 0x140C -+#define LANE_REG0504 0x1410 -+#define LANE_REG0505 0x1414 -+#define LANE_REG0506 0x1418 -+#define LANE_REG0507 0x141C -+#define LANE_REG0508 0x1420 -+#define LANE_REG0509 0x1424 -+#define LANE_REG050A 0x1428 -+#define LANE_REG050B 0x142C -+#define LANE_REG050C 0x1430 -+#define LANE_REG050D 0x1434 -+#define LANE_REG050E 0x1438 -+#define LANE_REG050F 0x143C -+#define LANE_REG0510 0x1440 -+#define LANE_REG0511 0x1444 -+#define LANE_REG0512 0x1448 -+#define LN2_TX_SER_RATE_SEL_RBR BIT(5) -+#define LN2_TX_SER_RATE_SEL_HBR BIT(4) -+#define LN2_TX_SER_RATE_SEL_HBR2 BIT(3) -+#define LN2_TX_SER_RATE_SEL_HBR3 BIT(2) -+#define LANE_REG0513 0x144C -+#define LANE_REG0514 0x1450 -+#define LANE_REG0515 0x1454 -+#define LANE_REG0516 0x1458 -+#define LANE_REG0517 0x145C -+#define LANE_REG0518 0x1460 -+#define LANE_REG0519 0x1464 -+#define LANE_REG051A 0x1468 -+#define LANE_REG051B 0x146C -+#define LANE_REG051C 0x1470 -+#define LANE_REG051D 0x1474 -+#define LANE_REG051E 0x1478 -+#define LANE_REG051F 0x147C -+#define LANE_REG0520 0x1480 -+#define LANE_REG0521 0x1484 -+#define LANE_REG0522 0x1488 -+#define LANE_REG0523 0x148C -+#define LANE_REG0524 0x1490 -+#define LANE_REG0525 0x1494 -+#define LANE_REG0526 0x1498 -+#define LANE_REG0527 0x149C -+#define LANE_REG0528 0x14A0 -+#define LANE_REG0529 0x14AD -+#define LANE_REG052A 0x14A8 -+#define LANE_REG052B 0x14AC -+#define LANE_REG052C 0x14B0 -+#define LANE_REG052D 0x14B4 -+#define LANE_REG0600 0x1800 -+#define LANE_REG0601 0x1804 -+#define LANE_REG0602 0x1808 -+#define LANE_REG0603 0x180C -+#define LANE_REG0604 0x1810 -+#define LANE_REG0605 0x1814 -+#define LANE_REG0606 0x1818 -+#define LANE_REG0607 0x181C -+#define LANE_REG0608 0x1820 -+#define LANE_REG0609 0x1824 -+#define LANE_REG060A 0x1828 -+#define LANE_REG060B 0x182C -+#define LANE_REG060C 0x1830 -+#define LANE_REG060D 0x1834 -+#define LANE_REG060E 0x1838 -+#define LANE_REG060F 0x183C -+#define LANE_REG0610 0x1840 -+#define LANE_REG0611 0x1844 -+#define LANE_REG0612 0x1848 -+#define LN3_TX_SER_RATE_SEL_RBR BIT(5) -+#define LN3_TX_SER_RATE_SEL_HBR BIT(4) -+#define LN3_TX_SER_RATE_SEL_HBR2 BIT(3) -+#define LN3_TX_SER_RATE_SEL_HBR3 BIT(2) -+#define LANE_REG0613 0x184C -+#define LANE_REG0614 0x1850 -+#define LANE_REG0615 0x1854 -+#define LANE_REG0616 0x1858 -+#define LANE_REG0617 0x185C -+#define LANE_REG0618 0x1860 -+#define LANE_REG0619 0x1864 -+#define LANE_REG061A 0x1868 -+#define LANE_REG061B 0x186C -+#define LANE_REG061C 0x1870 -+#define LANE_REG061D 0x1874 -+#define LANE_REG061E 0x1878 -+#define LANE_REG061F 0x187C -+#define LANE_REG0620 0x1880 -+#define LANE_REG0621 0x1884 -+#define LANE_REG0622 0x1888 -+#define LANE_REG0623 0x188C -+#define LANE_REG0624 0x1890 -+#define LANE_REG0625 0x1894 -+#define LANE_REG0626 0x1898 -+#define LANE_REG0627 0x189C -+#define LANE_REG0628 0x18A0 -+#define LANE_REG0629 0x18A4 -+#define LANE_REG062A 0x18A8 -+#define LANE_REG062B 0x18AC -+#define LANE_REG062C 0x18B0 -+#define LANE_REG062D 0x18B4 -+ -+#define HDMI20_MAX_RATE 600000000 -+#define DATA_RATE_MASK 0xFFFFFFF -+#define COLOR_DEPTH_MASK BIT(31) -+#define HDMI_MODE_MASK BIT(30) -+#define HDMI_EARC_MASK BIT(29) -+ -+enum hdptx_combphy_type { -+ SS_HDMI, -+ SS_DP -+}; -+ -+ -+struct lcpll_config { -+ u32 bit_rate; -+ u8 lcvco_mode_en; -+ u8 pi_en; -+ u8 clk_en_100m; -+ u8 pms_mdiv; -+ u8 pms_mdiv_afc; -+ u8 pms_pdiv; -+ u8 pms_refdiv; -+ u8 pms_sdiv; -+ u8 pi_cdiv_rstn; -+ u8 pi_cdiv_sel; -+ u8 sdm_en; -+ u8 sdm_rstn; -+ u8 sdc_frac_en; -+ u8 sdc_rstn; -+ u8 sdm_deno; -+ u8 sdm_num_sign; -+ u8 sdm_num; -+ u8 sdc_n; -+ u8 sdc_n2; -+ u8 sdc_num; -+ u8 sdc_deno; -+ u8 sdc_ndiv_rstn; -+ u8 ssc_en; -+ u8 ssc_fm_dev; -+ u8 ssc_fm_freq; -+ u8 ssc_clk_div_sel; -+ u8 cd_tx_ser_rate_sel; -+}; -+ -+struct ropll_config { -+ u32 bit_rate; -+ u8 pms_mdiv; -+ u8 pms_mdiv_afc; -+ u8 pms_pdiv; -+ u8 pms_refdiv; -+ u8 pms_sdiv; -+ u8 pms_iqdiv_rstn; -+ u8 ref_clk_sel; -+ u8 sdm_en; -+ u8 sdm_rstn; -+ u8 sdc_frac_en; -+ u8 sdc_rstn; -+ u8 sdm_clk_div; -+ u8 sdm_deno; -+ u8 sdm_num_sign; -+ u8 sdm_num; -+ u8 sdc_n; -+ u8 sdc_num; -+ u8 sdc_deno; -+ u8 sdc_ndiv_rstn; -+ u8 ssc_en; -+ u8 ssc_fm_dev; -+ u8 ssc_fm_freq; -+ u8 ssc_clk_div_sel; -+ u8 ana_cpp_ctrl; -+ u8 ana_lpf_c_sel; -+ u8 cd_tx_ser_rate_sel; -+}; -+ -+struct rockchip_hdptx_phy { -+ struct device *dev; -+ struct regmap *regmap; -+ struct regmap *grf; -+ -+ int irq; -+ int id; -+ -+ struct phy *phy; -+ struct clk_bulk_data *clks; -+ int nr_clks; -+ struct phy_config *phy_cfg; -+ -+ /* clk provider */ -+ struct clk_hw hw; -+ struct clk *dclk; -+ unsigned long rate; -+ -+ struct reset_control *phy_reset; -+ struct reset_control *apb_reset; -+ struct reset_control *cmn_reset; -+ struct reset_control *init_reset; -+ struct reset_control *lane_reset; -+ struct reset_control *ropll_reset; -+ struct reset_control *lcpll_reset; -+ -+ bool earc_en; -+ int count; -+}; -+ -+struct lcpll_config lcpll_cfg[] = { -+ { 48000000, 1, 0, 0, 0x7d, 0x7d, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 2, -+ 0, 0x13, 0x18, 1, 0, 0x20, 0x0c, 1, 0, -+ }, -+ { 40000000, 1, 1, 0, 0x68, 0x68, 1, 1, 0, 0, 0, 1, 1, 1, 1, 9, 0, 1, 1, -+ 0, 2, 3, 1, 0, 0x20, 0x0c, 1, 0, -+ }, -+ { 32000000, 1, 1, 1, 0x6b, 0x6b, 1, 1, 0, 1, 2, 1, 1, 1, 1, 9, 1, 2, 1, -+ 0, 0x0d, 0x18, 1, 0, 0x20, 0x0c, 1, 1, -+ }, -+ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, -+ }, -+}; -+ -+struct ropll_config ropll_frl_cfg[] = { -+ { 24000000, 0x19, 0x19, 1, 1, 0, 1, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, -+ 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 18000000, 0x7d, 0x7d, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, -+ 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 9000000, 0x7d, 0x7d, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, -+ 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, -+ }, -+}; -+ -+struct ropll_config ropll_tmds_cfg[] = { -+ { 5940000, 124, 124, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 3712500, 155, 155, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 2970000, 124, 124, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 1620000, 135, 135, 1, 1, 3, 1, 1, 0, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10, -+ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 1856250, 155, 155, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 1540000, 193, 193, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 193, 1, 32, 2, 1, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 1485000, 0x7b, 0x7b, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10, -+ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 1462500, 122, 122, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 244, 1, 16, 2, 1, 1, -+ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 1190000, 149, 149, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 149, 1, 16, 2, 1, 1, -+ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 1065000, 89, 89, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 89, 1, 16, 1, 0, 1, -+ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 1080000, 135, 135, 1, 1, 5, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14, -+ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 855000, 214, 214, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 214, 1, 16, 2, 1, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 835000, 105, 105, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 42, 1, 16, 1, 0, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 928125, 155, 155, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 742500, 124, 124, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 650000, 162, 162, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 54, 0, 16, 4, 1, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 337500, 0x70, 0x70, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 0x2, 0, 0x01, 5, 1, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 400000, 100, 100, 1, 1, 11, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14, -+ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 270000, 0x5a, 0x5a, 1, 1, 0xf, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14, -+ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { 251750, 84, 84, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 168, 1, 16, 4, 1, -+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, -+ }, -+ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, -+ }, -+}; -+ -+static bool rockchip_hdptx_phy_is_accissible_reg(struct device *dev, -+ unsigned int reg) -+{ -+ switch (reg) { -+ case 0x0000 ... 0x029c: -+ case 0x0400 ... 0x04a4: -+ case 0x0800 ... 0x08a4: -+ case 0x0c00 ... 0x0cb4: -+ case 0x1000 ... 0x10b4: -+ case 0x1400 ... 0x14b4: -+ case 0x1800 ... 0x18b4: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+static const struct regmap_config rockchip_hdptx_phy_regmap_config = { -+ .reg_bits = 32, -+ .reg_stride = 4, -+ .val_bits = 32, -+ .fast_io = true, -+ .max_register = 0x18b4, -+ .name = "hdptx-combphy", -+ -+ .readable_reg = rockchip_hdptx_phy_is_accissible_reg, -+ .writeable_reg = rockchip_hdptx_phy_is_accissible_reg, -+}; -+ -+static inline struct rockchip_hdptx_phy *to_rockchip_hdptx_phy(struct clk_hw *hw) -+{ -+ return container_of(hw, struct rockchip_hdptx_phy, hw); -+} -+ -+static inline void hdptx_write(struct rockchip_hdptx_phy *hdptx, u32 reg, u8 val) -+{ -+ regmap_write(hdptx->regmap, reg, val); -+} -+ -+static inline u8 hdptx_read(struct rockchip_hdptx_phy *hdptx, u32 reg) -+{ -+ u32 val; -+ -+ regmap_read(hdptx->regmap, reg, &val); -+ -+ return val; -+} -+ -+static inline void hdptx_update_bits(struct rockchip_hdptx_phy *hdptx, u32 reg, -+ u8 mask, u8 val) -+{ -+ regmap_update_bits(hdptx->regmap, reg, mask, val); -+} -+ -+static inline void hdptx_grf_write(struct rockchip_hdptx_phy *hdptx, u32 reg, u32 val) -+{ -+ regmap_write(hdptx->grf, reg, val); -+} -+ -+static inline u8 hdptx_grf_read(struct rockchip_hdptx_phy *hdptx, u32 reg) -+{ -+ u32 val; -+ -+ regmap_read(hdptx->grf, reg, &val); -+ -+ return val; -+} -+ -+static void hdptx_pre_power_up(struct rockchip_hdptx_phy *hdptx) -+{ -+ u32 val = 0; -+ -+ reset_control_assert(hdptx->apb_reset); -+ udelay(20); -+ reset_control_deassert(hdptx->apb_reset); -+ -+ reset_control_assert(hdptx->lane_reset); -+ reset_control_assert(hdptx->cmn_reset); -+ reset_control_assert(hdptx->init_reset); -+ -+ val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16; -+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); -+} -+ -+static int hdptx_post_enable_lane(struct rockchip_hdptx_phy *hdptx) -+{ -+ u32 val = 0; -+ int i; -+ -+ reset_control_deassert(hdptx->lane_reset); -+ -+ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN | -+ HDPTX_I_BGR_EN; -+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); -+ -+ for (i = 0; i < 50; i++) { -+ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); -+ -+ if (val & HDPTX_O_PHY_RDY && val & HDPTX_O_PLL_LOCK_DONE) -+ break; -+ udelay(100); -+ } -+ -+ if (i == 50) { -+ dev_err(hdptx->dev, "hdptx phy lane can't ready!\n"); -+ return -EINVAL; -+ } -+ -+ dev_err(hdptx->dev, "hdptx phy lane locked!\n"); -+ -+ return 0; -+} -+ -+static int hdptx_post_enable_pll(struct rockchip_hdptx_phy *hdptx) -+{ -+ u32 val = 0; -+ int i; -+ -+ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN | -+ HDPTX_I_BGR_EN; -+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); -+ udelay(10); -+ reset_control_deassert(hdptx->init_reset); -+ udelay(10); -+ val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN; -+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); -+ udelay(10); -+ reset_control_deassert(hdptx->cmn_reset); -+ -+ for (i = 0; i < 20; i++) { -+ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); -+ -+ if (val & HDPTX_O_PHY_CLK_RDY) -+ break; -+ udelay(20); -+ } -+ -+ if (i == 20) { -+ dev_err(hdptx->dev, "hdptx phy pll can't lock!\n"); -+ return -EINVAL; -+ } -+ -+ dev_err(hdptx->dev, "hdptx phy pll locked!\n"); -+ -+ return 0; -+} -+ -+static int hdptx_post_power_up(struct rockchip_hdptx_phy *hdptx) -+{ -+ u32 val = 0; -+ int i; -+ -+ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN | -+ HDPTX_I_BGR_EN; -+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); -+ udelay(10); -+ reset_control_deassert(hdptx->init_reset); -+ udelay(10); -+ val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN; -+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); -+ udelay(10); -+ reset_control_deassert(hdptx->cmn_reset); -+ -+ for (i = 0; i < 20; i++) { -+ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); -+ -+ if (val & HDPTX_O_PLL_LOCK_DONE) -+ break; -+ udelay(20); -+ } -+ -+ if (i == 20) { -+ dev_err(hdptx->dev, "hdptx phy can't lock!\n"); -+ return -EINVAL; -+ } -+ -+ udelay(20); -+ -+ reset_control_deassert(hdptx->lane_reset); -+ -+ for (i = 0; i < 50; i++) { -+ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); -+ -+ if (val & HDPTX_O_PHY_RDY) -+ break; -+ udelay(100); -+ } -+ -+ if (i == 50) { -+ dev_err(hdptx->dev, "hdptx phy can't ready!\n"); -+ return -EINVAL; -+ } -+ -+ dev_err(hdptx->dev, "hdptx phy locked!\n"); -+ -+ return 0; -+} -+ -+static void hdptx_phy_disable(struct rockchip_hdptx_phy *hdptx) -+{ -+ u32 val; -+ -+ /* reset phy and apb, or phy locked flag may keep 1 */ -+ reset_control_assert(hdptx->phy_reset); -+ udelay(20); -+ reset_control_deassert(hdptx->phy_reset); -+ -+ reset_control_assert(hdptx->apb_reset); -+ udelay(20); -+ reset_control_deassert(hdptx->apb_reset); -+ -+ hdptx_write(hdptx, LANE_REG0300, 0x82); -+ hdptx_write(hdptx, SB_REG010F, 0xc1); -+ hdptx_write(hdptx, SB_REG0110, 0x1); -+ hdptx_write(hdptx, LANE_REG0301, 0x80); -+ hdptx_write(hdptx, LANE_REG0401, 0x80); -+ hdptx_write(hdptx, LANE_REG0501, 0x80); -+ hdptx_write(hdptx, LANE_REG0601, 0x80); -+ -+ reset_control_assert(hdptx->lane_reset); -+ reset_control_assert(hdptx->cmn_reset); -+ reset_control_assert(hdptx->init_reset); -+ -+ val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16; -+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); -+} -+ -+static void hdptx_earc_config(struct rockchip_hdptx_phy *hdptx) -+{ -+ hdptx_update_bits(hdptx, SB_REG0113, SB_RX_RCAL_OPT_CODE_MASK, -+ SB_RX_RCAL_OPT_CODE(1)); -+ hdptx_write(hdptx, SB_REG011C, 0x04); -+ hdptx_update_bits(hdptx, SB_REG011B, SB_AFC_TOL_MASK, -+ SB_AFC_TOL(3)); -+ hdptx_write(hdptx, SB_REG0109, 0x05); -+ hdptx_update_bits(hdptx, SB_REG0120, SB_EARC_EN_MASK | SB_EARC_AFC_EN_MASK, -+ SB_EARC_EN(1) | SB_EARC_AFC_EN(1)); -+ hdptx_update_bits(hdptx, SB_REG011B, SB_EARC_SIG_DET_BYPASS_MASK, -+ SB_EARC_SIG_DET_BYPASS(1)); -+ hdptx_update_bits(hdptx, SB_REG011F, SB_PWM_AFC_CTRL_MASK | SB_RCAL_RSTN_MASK, -+ SB_PWM_AFC_CTRL(0xc) | SB_RCAL_RSTN(1)); -+ hdptx_update_bits(hdptx, SB_REG0115, SB_READY_DELAY_TIME_MASK, -+ SB_READY_DELAY_TIME(2)); -+ hdptx_update_bits(hdptx, SB_REG0113, SB_RX_RTERM_CTRL_MASK, -+ SB_RX_RTERM_CTRL(3)); -+ hdptx_update_bits(hdptx, SB_REG0102, ANA_SB_RXTERM_OFFSP_MASK, -+ ANA_SB_RXTERM_OFFSP(3)); -+ hdptx_update_bits(hdptx, SB_REG0103, ANA_SB_RXTERM_OFFSN_MASK, -+ ANA_SB_RXTERM_OFFSN(3)); -+ hdptx_write(hdptx, SB_REG011A, 0x03); -+ hdptx_write(hdptx, SB_REG0118, 0x0a); -+ hdptx_write(hdptx, SB_REG011E, 0x6a); -+ hdptx_write(hdptx, SB_REG011D, 0x67); -+ hdptx_update_bits(hdptx, SB_REG0117, FAST_PULSE_TIME_MASK, -+ FAST_PULSE_TIME(4)); -+ hdptx_update_bits(hdptx, SB_REG0114, SB_TG_SB_EN_DELAY_TIME_MASK | -+ SB_TG_RXTERM_EN_DELAY_TIME_MASK, -+ SB_TG_SB_EN_DELAY_TIME(2) | -+ SB_TG_RXTERM_EN_DELAY_TIME(2)); -+ hdptx_update_bits(hdptx, SB_REG0105, ANA_SB_TX_HLVL_PROG_MASK, -+ ANA_SB_TX_HLVL_PROG(7)); -+ hdptx_update_bits(hdptx, SB_REG0106, ANA_SB_TX_LLVL_PROG_MASK, -+ ANA_SB_TX_LLVL_PROG(7)); -+ hdptx_update_bits(hdptx, SB_REG010F, ANA_SB_VREG_GAIN_CTRL_MASK, -+ ANA_SB_VREG_GAIN_CTRL(0)); -+ hdptx_update_bits(hdptx, SB_REG0110, ANA_SB_VREG_REF_SEL_MASK, -+ ANA_SB_VREG_REF_SEL(1)); -+ hdptx_update_bits(hdptx, SB_REG0115, SB_TG_OSC_EN_DELAY_TIME_MASK, -+ SB_TG_OSC_EN_DELAY_TIME(2)); -+ hdptx_update_bits(hdptx, SB_REG0116, AFC_RSTN_DELAY_TIME_MASK, -+ AFC_RSTN_DELAY_TIME(2)); -+ hdptx_update_bits(hdptx, SB_REG0109, ANA_SB_DMRX_AFC_DIV_RATIO_MASK, -+ ANA_SB_DMRX_AFC_DIV_RATIO(5)); -+ hdptx_update_bits(hdptx, SB_REG0103, OVRD_SB_RX_RESCAL_DONE_MASK, -+ OVRD_SB_RX_RESCAL_DONE(1)); -+ hdptx_update_bits(hdptx, SB_REG0104, OVRD_SB_EN_MASK, -+ OVRD_SB_EN(1)); -+ hdptx_update_bits(hdptx, SB_REG0102, OVRD_SB_RXTERM_EN_MASK, -+ OVRD_SB_RXTERM_EN(1)); -+ hdptx_update_bits(hdptx, SB_REG0105, OVRD_SB_EARC_CMDC_EN_MASK, -+ OVRD_SB_EARC_CMDC_EN(1)); -+ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_EN_MASK | -+ OVRD_SB_VREG_LPF_BYPASS_MASK, -+ OVRD_SB_VREG_EN(1) | OVRD_SB_VREG_LPF_BYPASS(1)); -+ hdptx_update_bits(hdptx, SB_REG0123, OVRD_SB_READY_MASK, -+ OVRD_SB_READY(1)); -+ udelay(1000); -+ hdptx_update_bits(hdptx, SB_REG0103, SB_RX_RESCAL_DONE_MASK, -+ SB_RX_RESCAL_DONE(1)); -+ udelay(50); -+ hdptx_update_bits(hdptx, SB_REG0104, SB_EN_MASK, SB_EN(1)); -+ udelay(50); -+ hdptx_update_bits(hdptx, SB_REG0102, SB_RXTERM_EN_MASK, -+ SB_RXTERM_EN(1)); -+ udelay(50); -+ hdptx_update_bits(hdptx, SB_REG0105, SB_EARC_CMDC_EN_MASK, -+ SB_EARC_CMDC_EN(1)); -+ hdptx_update_bits(hdptx, SB_REG010F, SB_VREG_EN_MASK, -+ SB_VREG_EN(1)); -+ udelay(50); -+ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_LPF_BYPASS_MASK, -+ OVRD_SB_VREG_LPF_BYPASS(1)); -+ udelay(250); -+ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_LPF_BYPASS_MASK, -+ OVRD_SB_VREG_LPF_BYPASS(0)); -+ udelay(100); -+ hdptx_update_bits(hdptx, SB_REG0123, SB_READY_MASK, SB_READY(1)); -+} -+ -+static bool hdptx_phy_clk_pll_calc(unsigned int data_rate, -+ struct ropll_config *cfg) -+{ -+ unsigned int fref = 24000; -+ unsigned int sdc; -+ unsigned int fout = data_rate / 2; -+ unsigned int fvco; -+ u32 mdiv, sdiv, n = 8; -+ unsigned long k = 0, lc, k_sub, lc_sub; -+ -+ for (sdiv = 16; sdiv >= 1; sdiv--) { -+ if (sdiv % 2 && sdiv != 1) -+ continue; -+ -+ fvco = fout * sdiv; -+ -+ if (fvco < 2000000 || fvco > 4000000) -+ continue; -+ -+ mdiv = DIV_ROUND_UP(fvco, fref); -+ if (mdiv < 20 || mdiv > 255) -+ continue; -+ -+ if (fref * mdiv - fvco) { -+ for (sdc = 264000; sdc <= 750000; sdc += fref) -+ if (sdc * n > fref * mdiv) -+ break; -+ -+ if (sdc > 750000) -+ continue; -+ -+ rational_best_approximation(fref * mdiv - fvco, -+ sdc / 16, -+ GENMASK(6, 0), -+ GENMASK(7, 0), -+ &k, &lc); -+ -+ rational_best_approximation(sdc * n - fref * mdiv, -+ sdc, -+ GENMASK(6, 0), -+ GENMASK(7, 0), -+ &k_sub, &lc_sub); -+ } -+ -+ break; -+ } -+ -+ if (sdiv < 1) -+ return false; -+ -+ if (cfg) { -+ cfg->pms_mdiv = mdiv; -+ cfg->pms_mdiv_afc = mdiv; -+ cfg->pms_pdiv = 1; -+ cfg->pms_refdiv = 1; -+ cfg->pms_sdiv = sdiv - 1; -+ -+ cfg->sdm_en = k > 0 ? 1 : 0; -+ if (cfg->sdm_en) { -+ cfg->sdm_deno = lc; -+ cfg->sdm_num_sign = 1; -+ cfg->sdm_num = k; -+ cfg->sdc_n = n - 3; -+ cfg->sdc_num = k_sub; -+ cfg->sdc_deno = lc_sub; -+ } -+ } -+ -+ return true; -+} -+ -+static int hdptx_ropll_cmn_config(struct rockchip_hdptx_phy *hdptx, unsigned long bit_rate) -+{ -+ int bus_width = phy_get_bus_width(hdptx->phy); -+ u8 color_depth = (bus_width & COLOR_DEPTH_MASK) ? 1 : 0; -+ struct ropll_config *cfg = ropll_tmds_cfg; -+ struct ropll_config rc = {0}; -+ -+ dev_info(hdptx->dev, "%s bus_width:%x rate:%lu\n", __func__, bus_width, bit_rate); -+ hdptx->rate = bit_rate * 100; -+ -+ if (color_depth) -+ bit_rate = bit_rate * 10 / 8; -+ -+ for (; cfg->bit_rate != ~0; cfg++) -+ if (bit_rate == cfg->bit_rate) -+ break; -+ -+ if (cfg->bit_rate == ~0) { -+ if (hdptx_phy_clk_pll_calc(bit_rate, &rc)) { -+ cfg = &rc; -+ } else { -+ dev_err(hdptx->dev, "%s can't find pll cfg\n", __func__); -+ return -EINVAL; -+ } -+ } -+ -+ dev_dbg(hdptx->dev, "mdiv=%u, sdiv=%u\n", -+ cfg->pms_mdiv, cfg->pms_sdiv + 1); -+ dev_dbg(hdptx->dev, "sdm_en=%u, k_sign=%u, k=%u, lc=%u", -+ cfg->sdm_en, cfg->sdm_num_sign, cfg->sdm_num, cfg->sdm_deno); -+ dev_dbg(hdptx->dev, "n=%u, k_sub=%u, lc_sub=%u\n", -+ cfg->sdc_n + 3, cfg->sdc_num, cfg->sdc_deno); -+ -+ hdptx_pre_power_up(hdptx); -+ -+ reset_control_assert(hdptx->ropll_reset); -+ udelay(20); -+ reset_control_deassert(hdptx->ropll_reset); -+ -+ hdptx_write(hdptx, CMN_REG0008, 0x00); -+ hdptx_write(hdptx, CMN_REG0009, 0x0c); -+ hdptx_write(hdptx, CMN_REG000A, 0x83); -+ hdptx_write(hdptx, CMN_REG000B, 0x06); -+ hdptx_write(hdptx, CMN_REG000C, 0x20); -+ hdptx_write(hdptx, CMN_REG000D, 0xb8); -+ hdptx_write(hdptx, CMN_REG000E, 0x0f); -+ hdptx_write(hdptx, CMN_REG000F, 0x0f); -+ hdptx_write(hdptx, CMN_REG0010, 0x04); -+ hdptx_write(hdptx, CMN_REG0011, 0x01); -+ hdptx_write(hdptx, CMN_REG0012, 0x26); -+ hdptx_write(hdptx, CMN_REG0013, 0x22); -+ hdptx_write(hdptx, CMN_REG0014, 0x24); -+ hdptx_write(hdptx, CMN_REG0015, 0x77); -+ hdptx_write(hdptx, CMN_REG0016, 0x08); -+ hdptx_write(hdptx, CMN_REG0017, 0x20); -+ hdptx_write(hdptx, CMN_REG0018, 0x04); -+ hdptx_write(hdptx, CMN_REG0019, 0x48); -+ hdptx_write(hdptx, CMN_REG001A, 0x01); -+ hdptx_write(hdptx, CMN_REG001B, 0x00); -+ hdptx_write(hdptx, CMN_REG001C, 0x01); -+ hdptx_write(hdptx, CMN_REG001D, 0x64); -+ hdptx_write(hdptx, CMN_REG001E, 0x14); -+ hdptx_write(hdptx, CMN_REG001F, 0x00); -+ hdptx_write(hdptx, CMN_REG0020, 0x00); -+ hdptx_write(hdptx, CMN_REG0021, 0x00); -+ hdptx_write(hdptx, CMN_REG0022, 0x11); -+ hdptx_write(hdptx, CMN_REG0023, 0x00); -+ hdptx_write(hdptx, CMN_REG0024, 0x00); -+ hdptx_write(hdptx, CMN_REG0025, 0x53); -+ hdptx_write(hdptx, CMN_REG0026, 0x00); -+ hdptx_write(hdptx, CMN_REG0027, 0x00); -+ hdptx_write(hdptx, CMN_REG0028, 0x01); -+ hdptx_write(hdptx, CMN_REG0029, 0x01); -+ hdptx_write(hdptx, CMN_REG002A, 0x00); -+ hdptx_write(hdptx, CMN_REG002B, 0x00); -+ hdptx_write(hdptx, CMN_REG002C, 0x00); -+ hdptx_write(hdptx, CMN_REG002D, 0x00); -+ hdptx_write(hdptx, CMN_REG002E, 0x04); -+ hdptx_write(hdptx, CMN_REG002F, 0x00); -+ hdptx_write(hdptx, CMN_REG0030, 0x20); -+ hdptx_write(hdptx, CMN_REG0031, 0x30); -+ hdptx_write(hdptx, CMN_REG0032, 0x0b); -+ hdptx_write(hdptx, CMN_REG0033, 0x23); -+ hdptx_write(hdptx, CMN_REG0034, 0x00); -+ hdptx_write(hdptx, CMN_REG0035, 0x00); -+ hdptx_write(hdptx, CMN_REG0038, 0x00); -+ hdptx_write(hdptx, CMN_REG0039, 0x00); -+ hdptx_write(hdptx, CMN_REG003A, 0x00); -+ hdptx_write(hdptx, CMN_REG003B, 0x00); -+ hdptx_write(hdptx, CMN_REG003C, 0x80); -+ hdptx_write(hdptx, CMN_REG003D, 0x40); -+ hdptx_write(hdptx, CMN_REG003E, 0x0c); -+ hdptx_write(hdptx, CMN_REG003F, 0x83); -+ hdptx_write(hdptx, CMN_REG0040, 0x06); -+ hdptx_write(hdptx, CMN_REG0041, 0x20); -+ hdptx_write(hdptx, CMN_REG0042, 0x78); -+ hdptx_write(hdptx, CMN_REG0043, 0x00); -+ hdptx_write(hdptx, CMN_REG0044, 0x46); -+ hdptx_write(hdptx, CMN_REG0045, 0x24); -+ hdptx_write(hdptx, CMN_REG0046, 0xff); -+ hdptx_write(hdptx, CMN_REG0047, 0x00); -+ hdptx_write(hdptx, CMN_REG0048, 0x44); -+ hdptx_write(hdptx, CMN_REG0049, 0xfa); -+ hdptx_write(hdptx, CMN_REG004A, 0x08); -+ hdptx_write(hdptx, CMN_REG004B, 0x00); -+ hdptx_write(hdptx, CMN_REG004C, 0x01); -+ hdptx_write(hdptx, CMN_REG004D, 0x64); -+ hdptx_write(hdptx, CMN_REG004E, 0x34); -+ hdptx_write(hdptx, CMN_REG004F, 0x00); -+ hdptx_write(hdptx, CMN_REG0050, 0x00); -+ -+ hdptx_write(hdptx, CMN_REG0051, cfg->pms_mdiv); -+ hdptx_write(hdptx, CMN_REG0055, cfg->pms_mdiv_afc); -+ -+ hdptx_write(hdptx, CMN_REG0059, (cfg->pms_pdiv << 4) | cfg->pms_refdiv); -+ -+ hdptx_write(hdptx, CMN_REG005A, (cfg->pms_sdiv << 4)); -+ -+ hdptx_write(hdptx, CMN_REG005C, 0x25); -+ hdptx_write(hdptx, CMN_REG005D, 0x0c); -+ hdptx_write(hdptx, CMN_REG005E, 0x4f); -+ hdptx_update_bits(hdptx, CMN_REG005E, ROPLL_SDM_EN_MASK, -+ ROPLL_SDM_EN(cfg->sdm_en)); -+ if (!cfg->sdm_en) -+ hdptx_update_bits(hdptx, CMN_REG005E, 0xf, 0); -+ -+ hdptx_write(hdptx, CMN_REG005F, 0x01); -+ -+ hdptx_update_bits(hdptx, CMN_REG0064, ROPLL_SDM_NUM_SIGN_RBR_MASK, -+ ROPLL_SDM_NUM_SIGN_RBR(cfg->sdm_num_sign)); -+ hdptx_write(hdptx, CMN_REG0065, cfg->sdm_num); -+ hdptx_write(hdptx, CMN_REG0060, cfg->sdm_deno); -+ -+ hdptx_update_bits(hdptx, CMN_REG0069, ROPLL_SDC_N_RBR_MASK, -+ ROPLL_SDC_N_RBR(cfg->sdc_n)); -+ -+ hdptx_write(hdptx, CMN_REG006C, cfg->sdc_num); -+ hdptx_write(hdptx, CMN_REG0070, cfg->sdc_deno); -+ -+ hdptx_write(hdptx, CMN_REG006B, 0x04); -+ -+ hdptx_write(hdptx, CMN_REG0073, 0x30); -+ hdptx_write(hdptx, CMN_REG0074, 0x04); -+ hdptx_write(hdptx, CMN_REG0075, 0x20); -+ hdptx_write(hdptx, CMN_REG0076, 0x30); -+ hdptx_write(hdptx, CMN_REG0077, 0x08); -+ hdptx_write(hdptx, CMN_REG0078, 0x0c); -+ hdptx_write(hdptx, CMN_REG0079, 0x00); -+ hdptx_write(hdptx, CMN_REG007B, 0x00); -+ hdptx_write(hdptx, CMN_REG007C, 0x00); -+ hdptx_write(hdptx, CMN_REG007D, 0x00); -+ hdptx_write(hdptx, CMN_REG007E, 0x00); -+ hdptx_write(hdptx, CMN_REG007F, 0x00); -+ hdptx_write(hdptx, CMN_REG0080, 0x00); -+ hdptx_write(hdptx, CMN_REG0081, 0x01); -+ hdptx_write(hdptx, CMN_REG0082, 0x04); -+ hdptx_write(hdptx, CMN_REG0083, 0x24); -+ hdptx_write(hdptx, CMN_REG0084, 0x20); -+ hdptx_write(hdptx, CMN_REG0085, 0x03); -+ -+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK, -+ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv)); -+ -+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK, -+ PLL_PCG_CLK_SEL(color_depth)); -+ -+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_EN, PLL_PCG_CLK_EN); -+ -+ hdptx_write(hdptx, CMN_REG0087, 0x04); -+ hdptx_write(hdptx, CMN_REG0089, 0x00); -+ hdptx_write(hdptx, CMN_REG008A, 0x55); -+ hdptx_write(hdptx, CMN_REG008B, 0x25); -+ hdptx_write(hdptx, CMN_REG008C, 0x2c); -+ hdptx_write(hdptx, CMN_REG008D, 0x22); -+ hdptx_write(hdptx, CMN_REG008E, 0x14); -+ hdptx_write(hdptx, CMN_REG008F, 0x20); -+ hdptx_write(hdptx, CMN_REG0090, 0x00); -+ hdptx_write(hdptx, CMN_REG0091, 0x00); -+ hdptx_write(hdptx, CMN_REG0092, 0x00); -+ hdptx_write(hdptx, CMN_REG0093, 0x00); -+ hdptx_write(hdptx, CMN_REG0095, 0x00); -+ hdptx_write(hdptx, CMN_REG0097, 0x02); -+ hdptx_write(hdptx, CMN_REG0099, 0x04); -+ hdptx_write(hdptx, CMN_REG009A, 0x11); -+ hdptx_write(hdptx, CMN_REG009B, 0x00); -+ -+ return hdptx_post_enable_pll(hdptx); -+} -+ -+static int hdptx_ropll_tmds_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate) -+{ -+ u32 bit_rate = rate & DATA_RATE_MASK; -+ -+ if (!(hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE)) { -+ int ret; -+ -+ ret = hdptx_ropll_cmn_config(hdptx, bit_rate); -+ if (ret) -+ return ret; -+ } -+ -+ hdptx_write(hdptx, SB_REG0114, 0x00); -+ hdptx_write(hdptx, SB_REG0115, 0x00); -+ hdptx_write(hdptx, SB_REG0116, 0x00); -+ hdptx_write(hdptx, SB_REG0117, 0x00); -+ hdptx_write(hdptx, LNTOP_REG0200, 0x06); -+ -+ if (bit_rate >= 3400000) { -+ /* For 1/40 bitrate clk */ -+ hdptx_write(hdptx, LNTOP_REG0201, 0x00); -+ hdptx_write(hdptx, LNTOP_REG0202, 0x00); -+ hdptx_write(hdptx, LNTOP_REG0203, 0x0f); -+ hdptx_write(hdptx, LNTOP_REG0204, 0xff); -+ hdptx_write(hdptx, LNTOP_REG0205, 0xff); -+ } else { -+ /* For 1/10 bitrate clk */ -+ hdptx_write(hdptx, LNTOP_REG0201, 0x07); -+ hdptx_write(hdptx, LNTOP_REG0202, 0xc1); -+ hdptx_write(hdptx, LNTOP_REG0203, 0xf0); -+ hdptx_write(hdptx, LNTOP_REG0204, 0x7c); -+ hdptx_write(hdptx, LNTOP_REG0205, 0x1f); -+ } -+ -+ hdptx_write(hdptx, LNTOP_REG0206, 0x07); -+ hdptx_write(hdptx, LNTOP_REG0207, 0x0f); -+ hdptx_write(hdptx, LANE_REG0303, 0x0c); -+ hdptx_write(hdptx, LANE_REG0307, 0x20); -+ hdptx_write(hdptx, LANE_REG030A, 0x17); -+ hdptx_write(hdptx, LANE_REG030B, 0x77); -+ hdptx_write(hdptx, LANE_REG030C, 0x77); -+ hdptx_write(hdptx, LANE_REG030D, 0x77); -+ hdptx_write(hdptx, LANE_REG030E, 0x38); -+ hdptx_write(hdptx, LANE_REG0310, 0x03); -+ hdptx_write(hdptx, LANE_REG0311, 0x0f); -+ hdptx_write(hdptx, LANE_REG0312, 0x00); -+ hdptx_write(hdptx, LANE_REG0316, 0x02); -+ hdptx_write(hdptx, LANE_REG031B, 0x01); -+ hdptx_write(hdptx, LANE_REG031E, 0x00); -+ hdptx_write(hdptx, LANE_REG031F, 0x15); -+ hdptx_write(hdptx, LANE_REG0320, 0xa0); -+ hdptx_write(hdptx, LANE_REG0403, 0x0c); -+ hdptx_write(hdptx, LANE_REG0407, 0x20); -+ hdptx_write(hdptx, LANE_REG040A, 0x17); -+ hdptx_write(hdptx, LANE_REG040B, 0x77); -+ hdptx_write(hdptx, LANE_REG040C, 0x77); -+ hdptx_write(hdptx, LANE_REG040D, 0x77); -+ hdptx_write(hdptx, LANE_REG040E, 0x38); -+ hdptx_write(hdptx, LANE_REG0410, 0x03); -+ hdptx_write(hdptx, LANE_REG0411, 0x0f); -+ hdptx_write(hdptx, LANE_REG0412, 0x00); -+ hdptx_write(hdptx, LANE_REG0416, 0x02); -+ hdptx_write(hdptx, LANE_REG041B, 0x01); -+ hdptx_write(hdptx, LANE_REG041E, 0x00); -+ hdptx_write(hdptx, LANE_REG041F, 0x15); -+ hdptx_write(hdptx, LANE_REG0420, 0xa0); -+ hdptx_write(hdptx, LANE_REG0503, 0x0c); -+ hdptx_write(hdptx, LANE_REG0507, 0x20); -+ hdptx_write(hdptx, LANE_REG050A, 0x17); -+ hdptx_write(hdptx, LANE_REG050B, 0x77); -+ hdptx_write(hdptx, LANE_REG050C, 0x77); -+ hdptx_write(hdptx, LANE_REG050D, 0x77); -+ hdptx_write(hdptx, LANE_REG050E, 0x38); -+ hdptx_write(hdptx, LANE_REG0510, 0x03); -+ hdptx_write(hdptx, LANE_REG0511, 0x0f); -+ hdptx_write(hdptx, LANE_REG0512, 0x00); -+ hdptx_write(hdptx, LANE_REG0516, 0x02); -+ hdptx_write(hdptx, LANE_REG051B, 0x01); -+ hdptx_write(hdptx, LANE_REG051E, 0x00); -+ hdptx_write(hdptx, LANE_REG051F, 0x15); -+ hdptx_write(hdptx, LANE_REG0520, 0xa0); -+ hdptx_write(hdptx, LANE_REG0603, 0x0c); -+ hdptx_write(hdptx, LANE_REG0607, 0x20); -+ hdptx_write(hdptx, LANE_REG060A, 0x17); -+ hdptx_write(hdptx, LANE_REG060B, 0x77); -+ hdptx_write(hdptx, LANE_REG060C, 0x77); -+ hdptx_write(hdptx, LANE_REG060D, 0x77); -+ hdptx_write(hdptx, LANE_REG060E, 0x38); -+ hdptx_write(hdptx, LANE_REG0610, 0x03); -+ hdptx_write(hdptx, LANE_REG0611, 0x0f); -+ hdptx_write(hdptx, LANE_REG0612, 0x00); -+ hdptx_write(hdptx, LANE_REG0616, 0x02); -+ hdptx_write(hdptx, LANE_REG061B, 0x01); -+ hdptx_write(hdptx, LANE_REG061E, 0x08); -+ hdptx_write(hdptx, LANE_REG061F, 0x15); -+ hdptx_write(hdptx, LANE_REG0620, 0xa0); -+ -+ hdptx_write(hdptx, LANE_REG0303, 0x2f); -+ hdptx_write(hdptx, LANE_REG0403, 0x2f); -+ hdptx_write(hdptx, LANE_REG0503, 0x2f); -+ hdptx_write(hdptx, LANE_REG0603, 0x2f); -+ hdptx_write(hdptx, LANE_REG0305, 0x03); -+ hdptx_write(hdptx, LANE_REG0405, 0x03); -+ hdptx_write(hdptx, LANE_REG0505, 0x03); -+ hdptx_write(hdptx, LANE_REG0605, 0x03); -+ hdptx_write(hdptx, LANE_REG0306, 0x1c); -+ hdptx_write(hdptx, LANE_REG0406, 0x1c); -+ hdptx_write(hdptx, LANE_REG0506, 0x1c); -+ hdptx_write(hdptx, LANE_REG0606, 0x1c); -+ -+ if (hdptx->earc_en) -+ hdptx_earc_config(hdptx); -+ -+ return hdptx_post_enable_lane(hdptx); -+} -+ -+static int hdptx_ropll_frl_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate) -+{ -+ u32 bit_rate = rate & DATA_RATE_MASK; -+ u8 color_depth = (rate & COLOR_DEPTH_MASK) ? 1 : 0; -+ struct ropll_config *cfg = ropll_frl_cfg; -+ -+ for (; cfg->bit_rate != ~0; cfg++) -+ if (bit_rate == cfg->bit_rate) -+ break; -+ -+ if (cfg->bit_rate == ~0) { -+ dev_err(hdptx->dev, "%s can't find pll cfg\n", __func__); -+ return -EINVAL; -+ } -+ -+ hdptx_pre_power_up(hdptx); -+ -+ reset_control_assert(hdptx->ropll_reset); -+ usleep_range(10, 20); -+ reset_control_deassert(hdptx->ropll_reset); -+ -+ hdptx_write(hdptx, CMN_REG0008, 0x00); -+ hdptx_write(hdptx, CMN_REG0009, 0x0c); -+ hdptx_write(hdptx, CMN_REG000A, 0x83); -+ hdptx_write(hdptx, CMN_REG000B, 0x06); -+ hdptx_write(hdptx, CMN_REG000C, 0x20); -+ hdptx_write(hdptx, CMN_REG000D, 0xb8); -+ hdptx_write(hdptx, CMN_REG000E, 0x0f); -+ hdptx_write(hdptx, CMN_REG000F, 0x0f); -+ hdptx_write(hdptx, CMN_REG0010, 0x04); -+ hdptx_write(hdptx, CMN_REG0011, 0x00); -+ hdptx_write(hdptx, CMN_REG0012, 0x26); -+ hdptx_write(hdptx, CMN_REG0013, 0x22); -+ hdptx_write(hdptx, CMN_REG0014, 0x24); -+ hdptx_write(hdptx, CMN_REG0015, 0x77); -+ hdptx_write(hdptx, CMN_REG0016, 0x08); -+ hdptx_write(hdptx, CMN_REG0017, 0x00); -+ hdptx_write(hdptx, CMN_REG0018, 0x04); -+ hdptx_write(hdptx, CMN_REG0019, 0x48); -+ hdptx_write(hdptx, CMN_REG001A, 0x01); -+ hdptx_write(hdptx, CMN_REG001B, 0x00); -+ hdptx_write(hdptx, CMN_REG001C, 0x01); -+ hdptx_write(hdptx, CMN_REG001D, 0x64); -+ hdptx_write(hdptx, CMN_REG001E, 0x14); -+ hdptx_write(hdptx, CMN_REG001F, 0x00); -+ hdptx_write(hdptx, CMN_REG0020, 0x00); -+ hdptx_write(hdptx, CMN_REG0021, 0x00); -+ hdptx_write(hdptx, CMN_REG0022, 0x11); -+ hdptx_write(hdptx, CMN_REG0023, 0x00); -+ hdptx_write(hdptx, CMN_REG0025, 0x00); -+ hdptx_write(hdptx, CMN_REG0026, 0x53); -+ hdptx_write(hdptx, CMN_REG0027, 0x00); -+ hdptx_write(hdptx, CMN_REG0028, 0x00); -+ hdptx_write(hdptx, CMN_REG0029, 0x01); -+ hdptx_write(hdptx, CMN_REG002A, 0x01); -+ hdptx_write(hdptx, CMN_REG002B, 0x00); -+ hdptx_write(hdptx, CMN_REG002C, 0x00); -+ hdptx_write(hdptx, CMN_REG002D, 0x00); -+ hdptx_write(hdptx, CMN_REG002E, 0x00); -+ hdptx_write(hdptx, CMN_REG002F, 0x04); -+ hdptx_write(hdptx, CMN_REG0030, 0x00); -+ hdptx_write(hdptx, CMN_REG0031, 0x20); -+ hdptx_write(hdptx, CMN_REG0032, 0x30); -+ hdptx_write(hdptx, CMN_REG0033, 0x0b); -+ hdptx_write(hdptx, CMN_REG0034, 0x23); -+ hdptx_write(hdptx, CMN_REG0035, 0x00); -+ hdptx_write(hdptx, CMN_REG0038, 0x00); -+ hdptx_write(hdptx, CMN_REG0039, 0x00); -+ hdptx_write(hdptx, CMN_REG003A, 0x00); -+ hdptx_write(hdptx, CMN_REG003B, 0x00); -+ hdptx_write(hdptx, CMN_REG003C, 0x80); -+ hdptx_write(hdptx, CMN_REG003D, 0x40); -+ hdptx_write(hdptx, CMN_REG003E, 0x0c); -+ hdptx_write(hdptx, CMN_REG003F, 0x83); -+ hdptx_write(hdptx, CMN_REG0040, 0x06); -+ hdptx_write(hdptx, CMN_REG0041, 0x20); -+ hdptx_write(hdptx, CMN_REG0042, 0xb8); -+ hdptx_write(hdptx, CMN_REG0043, 0x00); -+ hdptx_write(hdptx, CMN_REG0044, 0x46); -+ hdptx_write(hdptx, CMN_REG0045, 0x24); -+ hdptx_write(hdptx, CMN_REG0046, 0xff); -+ hdptx_write(hdptx, CMN_REG0047, 0x00); -+ hdptx_write(hdptx, CMN_REG0048, 0x44); -+ hdptx_write(hdptx, CMN_REG0049, 0xfa); -+ hdptx_write(hdptx, CMN_REG004A, 0x08); -+ hdptx_write(hdptx, CMN_REG004B, 0x00); -+ hdptx_write(hdptx, CMN_REG004C, 0x01); -+ hdptx_write(hdptx, CMN_REG004D, 0x64); -+ hdptx_write(hdptx, CMN_REG004E, 0x14); -+ hdptx_write(hdptx, CMN_REG004F, 0x00); -+ hdptx_write(hdptx, CMN_REG0050, 0x00); -+ hdptx_write(hdptx, CMN_REG0051, cfg->pms_mdiv); -+ hdptx_write(hdptx, CMN_REG0055, cfg->pms_mdiv_afc); -+ hdptx_write(hdptx, CMN_REG0059, (cfg->pms_pdiv << 4) | cfg->pms_refdiv); -+ hdptx_write(hdptx, CMN_REG005A, (cfg->pms_sdiv << 4)); -+ hdptx_write(hdptx, CMN_REG005C, 0x25); -+ hdptx_write(hdptx, CMN_REG005D, 0x0c); -+ hdptx_update_bits(hdptx, CMN_REG005E, ROPLL_SDM_EN_MASK, -+ ROPLL_SDM_EN(cfg->sdm_en)); -+ if (!cfg->sdm_en) -+ hdptx_update_bits(hdptx, CMN_REG005E, 0xf, 0); -+ hdptx_write(hdptx, CMN_REG005F, 0x01); -+ hdptx_update_bits(hdptx, CMN_REG0064, ROPLL_SDM_NUM_SIGN_RBR_MASK, -+ ROPLL_SDM_NUM_SIGN_RBR(cfg->sdm_num_sign)); -+ hdptx_write(hdptx, CMN_REG0065, cfg->sdm_num); -+ hdptx_write(hdptx, CMN_REG0060, cfg->sdm_deno); -+ hdptx_update_bits(hdptx, CMN_REG0069, ROPLL_SDC_N_RBR_MASK, -+ ROPLL_SDC_N_RBR(cfg->sdc_n)); -+ hdptx_write(hdptx, CMN_REG006C, cfg->sdc_num); -+ hdptx_write(hdptx, CMN_REG0070, cfg->sdc_deno); -+ hdptx_write(hdptx, CMN_REG006B, 0x04); -+ hdptx_write(hdptx, CMN_REG0073, 0x30); -+ hdptx_write(hdptx, CMN_REG0074, 0x00); -+ hdptx_write(hdptx, CMN_REG0075, 0x20); -+ hdptx_write(hdptx, CMN_REG0076, 0x30); -+ hdptx_write(hdptx, CMN_REG0077, 0x08); -+ hdptx_write(hdptx, CMN_REG0078, 0x0c); -+ hdptx_write(hdptx, CMN_REG0079, 0x00); -+ hdptx_write(hdptx, CMN_REG007B, 0x00); -+ hdptx_write(hdptx, CMN_REG007C, 0x00); -+ hdptx_write(hdptx, CMN_REG007D, 0x00); -+ hdptx_write(hdptx, CMN_REG007E, 0x00); -+ hdptx_write(hdptx, CMN_REG007F, 0x00); -+ hdptx_write(hdptx, CMN_REG0080, 0x00); -+ hdptx_write(hdptx, CMN_REG0081, 0x09); -+ hdptx_write(hdptx, CMN_REG0082, 0x04); -+ hdptx_write(hdptx, CMN_REG0083, 0x24); -+ hdptx_write(hdptx, CMN_REG0084, 0x20); -+ hdptx_write(hdptx, CMN_REG0085, 0x03); -+ hdptx_write(hdptx, CMN_REG0086, 0x01); -+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK, -+ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv)); -+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK, -+ PLL_PCG_CLK_SEL(color_depth)); -+ hdptx_write(hdptx, CMN_REG0087, 0x0c); -+ hdptx_write(hdptx, CMN_REG0089, 0x00); -+ hdptx_write(hdptx, CMN_REG008A, 0x55); -+ hdptx_write(hdptx, CMN_REG008B, 0x25); -+ hdptx_write(hdptx, CMN_REG008C, 0x2c); -+ hdptx_write(hdptx, CMN_REG008D, 0x22); -+ hdptx_write(hdptx, CMN_REG008E, 0x14); -+ hdptx_write(hdptx, CMN_REG008F, 0x20); -+ hdptx_write(hdptx, CMN_REG0090, 0x00); -+ hdptx_write(hdptx, CMN_REG0091, 0x00); -+ hdptx_write(hdptx, CMN_REG0092, 0x00); -+ hdptx_write(hdptx, CMN_REG0093, 0x00); -+ hdptx_write(hdptx, CMN_REG0094, 0x00); -+ hdptx_write(hdptx, CMN_REG0097, 0x02); -+ hdptx_write(hdptx, CMN_REG0099, 0x04); -+ hdptx_write(hdptx, CMN_REG009A, 0x11); -+ hdptx_write(hdptx, CMN_REG009B, 0x10); -+ hdptx_write(hdptx, SB_REG0114, 0x00); -+ hdptx_write(hdptx, SB_REG0115, 0x00); -+ hdptx_write(hdptx, SB_REG0116, 0x00); -+ hdptx_write(hdptx, SB_REG0117, 0x00); -+ hdptx_write(hdptx, LNTOP_REG0200, 0x04); -+ hdptx_write(hdptx, LNTOP_REG0201, 0x00); -+ hdptx_write(hdptx, LNTOP_REG0202, 0x00); -+ hdptx_write(hdptx, LNTOP_REG0203, 0xf0); -+ hdptx_write(hdptx, LNTOP_REG0204, 0xff); -+ hdptx_write(hdptx, LNTOP_REG0205, 0xff); -+ hdptx_write(hdptx, LNTOP_REG0206, 0x05); -+ hdptx_write(hdptx, LNTOP_REG0207, 0x0f); -+ hdptx_write(hdptx, LANE_REG0303, 0x0c); -+ hdptx_write(hdptx, LANE_REG0307, 0x20); -+ hdptx_write(hdptx, LANE_REG030A, 0x17); -+ hdptx_write(hdptx, LANE_REG030B, 0x77); -+ hdptx_write(hdptx, LANE_REG030C, 0x77); -+ hdptx_write(hdptx, LANE_REG030D, 0x77); -+ hdptx_write(hdptx, LANE_REG030E, 0x38); -+ hdptx_write(hdptx, LANE_REG0310, 0x03); -+ hdptx_write(hdptx, LANE_REG0311, 0x0f); -+ hdptx_write(hdptx, LANE_REG0312, 0x3c); -+ hdptx_write(hdptx, LANE_REG0316, 0x02); -+ hdptx_write(hdptx, LANE_REG031B, 0x01); -+ hdptx_write(hdptx, LANE_REG031F, 0x15); -+ hdptx_write(hdptx, LANE_REG0320, 0xa0); -+ hdptx_write(hdptx, LANE_REG0403, 0x0c); -+ hdptx_write(hdptx, LANE_REG0407, 0x20); -+ hdptx_write(hdptx, LANE_REG040A, 0x17); -+ hdptx_write(hdptx, LANE_REG040B, 0x77); -+ hdptx_write(hdptx, LANE_REG040C, 0x77); -+ hdptx_write(hdptx, LANE_REG040D, 0x77); -+ hdptx_write(hdptx, LANE_REG040E, 0x38); -+ hdptx_write(hdptx, LANE_REG0410, 0x03); -+ hdptx_write(hdptx, LANE_REG0411, 0x0f); -+ hdptx_write(hdptx, LANE_REG0412, 0x3c); -+ hdptx_write(hdptx, LANE_REG0416, 0x02); -+ hdptx_write(hdptx, LANE_REG041B, 0x01); -+ hdptx_write(hdptx, LANE_REG041F, 0x15); -+ hdptx_write(hdptx, LANE_REG0420, 0xa0); -+ hdptx_write(hdptx, LANE_REG0503, 0x0c); -+ hdptx_write(hdptx, LANE_REG0507, 0x20); -+ hdptx_write(hdptx, LANE_REG050A, 0x17); -+ hdptx_write(hdptx, LANE_REG050B, 0x77); -+ hdptx_write(hdptx, LANE_REG050C, 0x77); -+ hdptx_write(hdptx, LANE_REG050D, 0x77); -+ hdptx_write(hdptx, LANE_REG050E, 0x38); -+ hdptx_write(hdptx, LANE_REG0510, 0x03); -+ hdptx_write(hdptx, LANE_REG0511, 0x0f); -+ hdptx_write(hdptx, LANE_REG0512, 0x3c); -+ hdptx_write(hdptx, LANE_REG0516, 0x02); -+ hdptx_write(hdptx, LANE_REG051B, 0x01); -+ hdptx_write(hdptx, LANE_REG051F, 0x15); -+ hdptx_write(hdptx, LANE_REG0520, 0xa0); -+ hdptx_write(hdptx, LANE_REG0603, 0x0c); -+ hdptx_write(hdptx, LANE_REG0607, 0x20); -+ hdptx_write(hdptx, LANE_REG060A, 0x17); -+ hdptx_write(hdptx, LANE_REG060B, 0x77); -+ hdptx_write(hdptx, LANE_REG060C, 0x77); -+ hdptx_write(hdptx, LANE_REG060D, 0x77); -+ hdptx_write(hdptx, LANE_REG060E, 0x38); -+ hdptx_write(hdptx, LANE_REG0610, 0x03); -+ hdptx_write(hdptx, LANE_REG0611, 0x0f); -+ hdptx_write(hdptx, LANE_REG0612, 0x3c); -+ hdptx_write(hdptx, LANE_REG0616, 0x02); -+ hdptx_write(hdptx, LANE_REG061B, 0x01); -+ hdptx_write(hdptx, LANE_REG061F, 0x15); -+ hdptx_write(hdptx, LANE_REG0620, 0xa0); -+ -+ if (hdptx->earc_en) -+ hdptx_earc_config(hdptx); -+ -+ return hdptx_post_power_up(hdptx); -+} -+ -+static int hdptx_lcpll_frl_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate) -+{ -+ u32 bit_rate = rate & DATA_RATE_MASK; -+ u8 color_depth = (rate & COLOR_DEPTH_MASK) ? 1 : 0; -+ struct lcpll_config *cfg = lcpll_cfg; -+ -+ for (; cfg->bit_rate != ~0; cfg++) -+ if (bit_rate == cfg->bit_rate) -+ break; -+ -+ if (cfg->bit_rate == ~0) -+ return -EINVAL; -+ -+ hdptx_pre_power_up(hdptx); -+ -+ hdptx_update_bits(hdptx, CMN_REG0008, LCPLL_EN_MASK | -+ LCPLL_LCVCO_MODE_EN_MASK, LCPLL_EN(1) | -+ LCPLL_LCVCO_MODE_EN(cfg->lcvco_mode_en)); -+ hdptx_write(hdptx, CMN_REG0009, 0x0c); -+ hdptx_write(hdptx, CMN_REG000A, 0x83); -+ hdptx_write(hdptx, CMN_REG000B, 0x06); -+ hdptx_write(hdptx, CMN_REG000C, 0x20); -+ hdptx_write(hdptx, CMN_REG000D, 0xb8); -+ hdptx_write(hdptx, CMN_REG000E, 0x0f); -+ hdptx_write(hdptx, CMN_REG000F, 0x0f); -+ hdptx_write(hdptx, CMN_REG0010, 0x04); -+ hdptx_write(hdptx, CMN_REG0011, 0x00); -+ hdptx_write(hdptx, CMN_REG0012, 0x26); -+ hdptx_write(hdptx, CMN_REG0013, 0x22); -+ hdptx_write(hdptx, CMN_REG0014, 0x24); -+ hdptx_write(hdptx, CMN_REG0015, 0x77); -+ hdptx_write(hdptx, CMN_REG0016, 0x08); -+ hdptx_write(hdptx, CMN_REG0017, 0x00); -+ hdptx_write(hdptx, CMN_REG0018, 0x04); -+ hdptx_write(hdptx, CMN_REG0019, 0x48); -+ hdptx_write(hdptx, CMN_REG001A, 0x01); -+ hdptx_write(hdptx, CMN_REG001B, 0x00); -+ hdptx_write(hdptx, CMN_REG001C, 0x01); -+ hdptx_write(hdptx, CMN_REG001D, 0x64); -+ hdptx_update_bits(hdptx, CMN_REG001E, LCPLL_PI_EN_MASK | -+ LCPLL_100M_CLK_EN_MASK, -+ LCPLL_PI_EN(cfg->pi_en) | -+ LCPLL_100M_CLK_EN(cfg->clk_en_100m)); -+ hdptx_write(hdptx, CMN_REG001F, 0x00); -+ hdptx_write(hdptx, CMN_REG0020, cfg->pms_mdiv); -+ hdptx_write(hdptx, CMN_REG0021, cfg->pms_mdiv_afc); -+ hdptx_write(hdptx, CMN_REG0022, (cfg->pms_pdiv << 4) | cfg->pms_refdiv); -+ hdptx_write(hdptx, CMN_REG0023, (cfg->pms_sdiv << 4) | cfg->pms_sdiv); -+ hdptx_write(hdptx, CMN_REG0025, 0x10); -+ hdptx_write(hdptx, CMN_REG0026, 0x53); -+ hdptx_write(hdptx, CMN_REG0027, 0x01); -+ hdptx_write(hdptx, CMN_REG0028, 0x0d); -+ hdptx_write(hdptx, CMN_REG0029, 0x01); -+ hdptx_write(hdptx, CMN_REG002A, cfg->sdm_deno); -+ hdptx_write(hdptx, CMN_REG002B, cfg->sdm_num_sign); -+ hdptx_write(hdptx, CMN_REG002C, cfg->sdm_num); -+ hdptx_update_bits(hdptx, CMN_REG002D, LCPLL_SDC_N_MASK, -+ LCPLL_SDC_N(cfg->sdc_n)); -+ hdptx_write(hdptx, CMN_REG002E, 0x02); -+ hdptx_write(hdptx, CMN_REG002F, 0x0d); -+ hdptx_write(hdptx, CMN_REG0030, 0x00); -+ hdptx_write(hdptx, CMN_REG0031, 0x20); -+ hdptx_write(hdptx, CMN_REG0032, 0x30); -+ hdptx_write(hdptx, CMN_REG0033, 0x0b); -+ hdptx_write(hdptx, CMN_REG0034, 0x23); -+ hdptx_write(hdptx, CMN_REG0035, 0x00); -+ hdptx_write(hdptx, CMN_REG0038, 0x00); -+ hdptx_write(hdptx, CMN_REG0039, 0x00); -+ hdptx_write(hdptx, CMN_REG003A, 0x00); -+ hdptx_write(hdptx, CMN_REG003B, 0x00); -+ hdptx_write(hdptx, CMN_REG003C, 0x80); -+ hdptx_write(hdptx, CMN_REG003D, 0x00); -+ hdptx_write(hdptx, CMN_REG003E, 0x0c); -+ hdptx_write(hdptx, CMN_REG003F, 0x83); -+ hdptx_write(hdptx, CMN_REG0040, 0x06); -+ hdptx_write(hdptx, CMN_REG0041, 0x20); -+ hdptx_write(hdptx, CMN_REG0042, 0xb8); -+ hdptx_write(hdptx, CMN_REG0043, 0x00); -+ hdptx_write(hdptx, CMN_REG0044, 0x46); -+ hdptx_write(hdptx, CMN_REG0045, 0x24); -+ hdptx_write(hdptx, CMN_REG0046, 0xff); -+ hdptx_write(hdptx, CMN_REG0047, 0x00); -+ hdptx_write(hdptx, CMN_REG0048, 0x44); -+ hdptx_write(hdptx, CMN_REG0049, 0xfa); -+ hdptx_write(hdptx, CMN_REG004A, 0x08); -+ hdptx_write(hdptx, CMN_REG004B, 0x00); -+ hdptx_write(hdptx, CMN_REG004C, 0x01); -+ hdptx_write(hdptx, CMN_REG004D, 0x64); -+ hdptx_write(hdptx, CMN_REG004E, 0x14); -+ hdptx_write(hdptx, CMN_REG004F, 0x00); -+ hdptx_write(hdptx, CMN_REG0050, 0x00); -+ hdptx_write(hdptx, CMN_REG0051, 0x00); -+ hdptx_write(hdptx, CMN_REG0055, 0x00); -+ hdptx_write(hdptx, CMN_REG0059, 0x11); -+ hdptx_write(hdptx, CMN_REG005A, 0x03); -+ hdptx_write(hdptx, CMN_REG005C, 0x05); -+ hdptx_write(hdptx, CMN_REG005D, 0x0c); -+ hdptx_write(hdptx, CMN_REG005E, 0x07); -+ hdptx_write(hdptx, CMN_REG005F, 0x01); -+ hdptx_write(hdptx, CMN_REG0060, 0x01); -+ hdptx_write(hdptx, CMN_REG0064, 0x07); -+ hdptx_write(hdptx, CMN_REG0065, 0x00); -+ hdptx_write(hdptx, CMN_REG0069, 0x00); -+ hdptx_write(hdptx, CMN_REG006B, 0x04); -+ hdptx_write(hdptx, CMN_REG006C, 0x00); -+ hdptx_write(hdptx, CMN_REG0070, 0x01); -+ hdptx_write(hdptx, CMN_REG0073, 0x30); -+ hdptx_write(hdptx, CMN_REG0074, 0x00); -+ hdptx_write(hdptx, CMN_REG0075, 0x20); -+ hdptx_write(hdptx, CMN_REG0076, 0x30); -+ hdptx_write(hdptx, CMN_REG0077, 0x08); -+ hdptx_write(hdptx, CMN_REG0078, 0x0c); -+ hdptx_write(hdptx, CMN_REG0079, 0x00); -+ hdptx_write(hdptx, CMN_REG007B, 0x00); -+ hdptx_write(hdptx, CMN_REG007C, 0x00); -+ hdptx_write(hdptx, CMN_REG007D, 0x00); -+ hdptx_write(hdptx, CMN_REG007E, 0x00); -+ hdptx_write(hdptx, CMN_REG007F, 0x00); -+ hdptx_write(hdptx, CMN_REG0080, 0x00); -+ hdptx_write(hdptx, CMN_REG0081, 0x09); -+ hdptx_write(hdptx, CMN_REG0082, 0x04); -+ hdptx_write(hdptx, CMN_REG0083, 0x24); -+ hdptx_write(hdptx, CMN_REG0084, 0x20); -+ hdptx_write(hdptx, CMN_REG0085, 0x03); -+ hdptx_write(hdptx, CMN_REG0086, 0x01); -+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK, -+ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv)); -+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK, -+ PLL_PCG_CLK_SEL(color_depth)); -+ hdptx_write(hdptx, CMN_REG0087, 0x0c); -+ hdptx_write(hdptx, CMN_REG0089, 0x02); -+ hdptx_write(hdptx, CMN_REG008A, 0x55); -+ hdptx_write(hdptx, CMN_REG008B, 0x25); -+ hdptx_write(hdptx, CMN_REG008C, 0x2c); -+ hdptx_write(hdptx, CMN_REG008D, 0x22); -+ hdptx_write(hdptx, CMN_REG008E, 0x14); -+ hdptx_write(hdptx, CMN_REG008F, 0x20); -+ hdptx_write(hdptx, CMN_REG0090, 0x00); -+ hdptx_write(hdptx, CMN_REG0091, 0x00); -+ hdptx_write(hdptx, CMN_REG0092, 0x00); -+ hdptx_write(hdptx, CMN_REG0093, 0x00); -+ hdptx_write(hdptx, CMN_REG0095, 0x00); -+ hdptx_write(hdptx, CMN_REG0097, 0x00); -+ hdptx_write(hdptx, CMN_REG0099, 0x00); -+ hdptx_write(hdptx, CMN_REG009A, 0x11); -+ hdptx_write(hdptx, CMN_REG009B, 0x10); -+ hdptx_write(hdptx, SB_REG0114, 0x00); -+ hdptx_write(hdptx, SB_REG0115, 0x00); -+ hdptx_write(hdptx, SB_REG0116, 0x00); -+ hdptx_write(hdptx, SB_REG0117, 0x00); -+ hdptx_write(hdptx, LNTOP_REG0200, 0x04); -+ hdptx_write(hdptx, LNTOP_REG0201, 0x00); -+ hdptx_write(hdptx, LNTOP_REG0202, 0x00); -+ hdptx_write(hdptx, LNTOP_REG0203, 0xf0); -+ hdptx_write(hdptx, LNTOP_REG0204, 0xff); -+ hdptx_write(hdptx, LNTOP_REG0205, 0xff); -+ hdptx_write(hdptx, LNTOP_REG0206, 0x05); -+ hdptx_write(hdptx, LNTOP_REG0207, 0x0f); -+ hdptx_write(hdptx, LANE_REG0303, 0x0c); -+ hdptx_write(hdptx, LANE_REG0307, 0x20); -+ hdptx_write(hdptx, LANE_REG030A, 0x17); -+ hdptx_write(hdptx, LANE_REG030B, 0x77); -+ hdptx_write(hdptx, LANE_REG030C, 0x77); -+ hdptx_write(hdptx, LANE_REG030D, 0x77); -+ hdptx_write(hdptx, LANE_REG030E, 0x38); -+ hdptx_write(hdptx, LANE_REG0310, 0x03); -+ hdptx_write(hdptx, LANE_REG0311, 0x0f); -+ hdptx_write(hdptx, LANE_REG0312, 0x3c); -+ hdptx_write(hdptx, LANE_REG0316, 0x02); -+ hdptx_write(hdptx, LANE_REG031B, 0x01); -+ hdptx_write(hdptx, LANE_REG031F, 0x15); -+ hdptx_write(hdptx, LANE_REG0320, 0xa0); -+ hdptx_write(hdptx, LANE_REG0403, 0x0c); -+ hdptx_write(hdptx, LANE_REG0407, 0x20); -+ hdptx_write(hdptx, LANE_REG040A, 0x17); -+ hdptx_write(hdptx, LANE_REG040B, 0x77); -+ hdptx_write(hdptx, LANE_REG040C, 0x77); -+ hdptx_write(hdptx, LANE_REG040D, 0x77); -+ hdptx_write(hdptx, LANE_REG040E, 0x38); -+ hdptx_write(hdptx, LANE_REG0410, 0x03); -+ hdptx_write(hdptx, LANE_REG0411, 0x0f); -+ hdptx_write(hdptx, LANE_REG0412, 0x3c); -+ hdptx_write(hdptx, LANE_REG0416, 0x02); -+ hdptx_write(hdptx, LANE_REG041B, 0x01); -+ hdptx_write(hdptx, LANE_REG041F, 0x15); -+ hdptx_write(hdptx, LANE_REG0420, 0xa0); -+ hdptx_write(hdptx, LANE_REG0503, 0x0c); -+ hdptx_write(hdptx, LANE_REG0507, 0x20); -+ hdptx_write(hdptx, LANE_REG050A, 0x17); -+ hdptx_write(hdptx, LANE_REG050B, 0x77); -+ hdptx_write(hdptx, LANE_REG050C, 0x77); -+ hdptx_write(hdptx, LANE_REG050D, 0x77); -+ hdptx_write(hdptx, LANE_REG050E, 0x38); -+ hdptx_write(hdptx, LANE_REG0510, 0x03); -+ hdptx_write(hdptx, LANE_REG0511, 0x0f); -+ hdptx_write(hdptx, LANE_REG0512, 0x3c); -+ hdptx_write(hdptx, LANE_REG0516, 0x02); -+ hdptx_write(hdptx, LANE_REG051B, 0x01); -+ hdptx_write(hdptx, LANE_REG051F, 0x15); -+ hdptx_write(hdptx, LANE_REG0520, 0xa0); -+ hdptx_write(hdptx, LANE_REG0603, 0x0c); -+ hdptx_write(hdptx, LANE_REG0607, 0x20); -+ hdptx_write(hdptx, LANE_REG060A, 0x17); -+ hdptx_write(hdptx, LANE_REG060B, 0x77); -+ hdptx_write(hdptx, LANE_REG060C, 0x77); -+ hdptx_write(hdptx, LANE_REG060D, 0x77); -+ hdptx_write(hdptx, LANE_REG060E, 0x38); -+ hdptx_write(hdptx, LANE_REG0610, 0x03); -+ hdptx_write(hdptx, LANE_REG0611, 0x0f); -+ hdptx_write(hdptx, LANE_REG0612, 0x3c); -+ hdptx_write(hdptx, LANE_REG0616, 0x02); -+ hdptx_write(hdptx, LANE_REG061B, 0x01); -+ hdptx_write(hdptx, LANE_REG061F, 0x15); -+ hdptx_write(hdptx, LANE_REG0620, 0xa0); -+ -+ hdptx_write(hdptx, LANE_REG0303, 0x2f); -+ hdptx_write(hdptx, LANE_REG0403, 0x2f); -+ hdptx_write(hdptx, LANE_REG0503, 0x2f); -+ hdptx_write(hdptx, LANE_REG0603, 0x2f); -+ hdptx_write(hdptx, LANE_REG0305, 0x03); -+ hdptx_write(hdptx, LANE_REG0405, 0x03); -+ hdptx_write(hdptx, LANE_REG0505, 0x03); -+ hdptx_write(hdptx, LANE_REG0605, 0x03); -+ hdptx_write(hdptx, LANE_REG0306, 0xfc); -+ hdptx_write(hdptx, LANE_REG0406, 0xfc); -+ hdptx_write(hdptx, LANE_REG0506, 0xfc); -+ hdptx_write(hdptx, LANE_REG0606, 0xfc); -+ -+ hdptx_write(hdptx, LANE_REG0305, 0x4f); -+ hdptx_write(hdptx, LANE_REG0405, 0x4f); -+ hdptx_write(hdptx, LANE_REG0505, 0x4f); -+ hdptx_write(hdptx, LANE_REG0605, 0x4f); -+ hdptx_write(hdptx, LANE_REG0304, 0x14); -+ hdptx_write(hdptx, LANE_REG0404, 0x14); -+ hdptx_write(hdptx, LANE_REG0504, 0x14); -+ hdptx_write(hdptx, LANE_REG0604, 0x14); -+ -+ if (hdptx->earc_en) -+ hdptx_earc_config(hdptx); -+ -+ return hdptx_post_power_up(hdptx); -+} -+ -+static int rockchip_hdptx_phy_power_on(struct phy *phy) -+{ -+ struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy); -+ int bus_width = phy_get_bus_width(hdptx->phy); -+ int bit_rate = bus_width & DATA_RATE_MASK; -+ int ret; -+ -+ if (!hdptx->count) { -+ ret = clk_bulk_enable(hdptx->nr_clks, hdptx->clks); -+ if (ret) { -+ dev_err(hdptx->dev, "failed to enable clocks\n"); -+ return ret; -+ } -+ } -+ -+ dev_info(hdptx->dev, "bus_width:0x%x,bit_rate:%d\n", bus_width, bit_rate); -+ if (bus_width & HDMI_EARC_MASK) -+ hdptx->earc_en = true; -+ else -+ hdptx->earc_en = false; -+ -+ if (bus_width & HDMI_MODE_MASK) { -+ if (bit_rate > 24000000) -+ return hdptx_lcpll_frl_mode_config(hdptx, bus_width); -+ else -+ return hdptx_ropll_frl_mode_config(hdptx, bus_width); -+ } else { -+ return hdptx_ropll_tmds_mode_config(hdptx, bus_width); -+ } -+} -+ -+static int rockchip_hdptx_phy_power_off(struct phy *phy) -+{ -+ struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy); -+ -+ if (hdptx->count) -+ return 0; -+ -+ if (!(hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE)) -+ return 0; -+ -+ hdptx_phy_disable(hdptx); -+ clk_bulk_disable(hdptx->nr_clks, hdptx->clks); -+ -+ return 0; -+} -+ -+static const struct phy_ops rockchip_hdptx_phy_ops = { -+ .owner = THIS_MODULE, -+ .power_on = rockchip_hdptx_phy_power_on, -+ .power_off = rockchip_hdptx_phy_power_off, -+}; -+ -+static const struct of_device_id rockchip_hdptx_phy_of_match[] = { -+ { .compatible = "rockchip,rk3588-hdptx-phy-hdmi", -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, rockchip_hdptx_phy_of_match); -+ -+static void rockchip_hdptx_phy_runtime_disable(void *data) -+{ -+ struct rockchip_hdptx_phy *hdptx = data; -+ -+ clk_bulk_unprepare(hdptx->nr_clks, hdptx->clks); -+ pm_runtime_disable(hdptx->dev); -+} -+ -+static unsigned long hdptx_phy_clk_recalc_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); -+ -+ return hdptx->rate; -+} -+ -+static long hdptx_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long *parent_rate) -+{ -+ struct ropll_config *cfg = ropll_tmds_cfg; -+ u32 bit_rate = rate / 100; -+ -+ if (rate > HDMI20_MAX_RATE) -+ return rate; -+ -+ for (; cfg->bit_rate != ~0; cfg++) -+ if (bit_rate == cfg->bit_rate) -+ break; -+ -+ if (cfg->bit_rate == ~0 && !hdptx_phy_clk_pll_calc(bit_rate, NULL)) -+ return -EINVAL; -+ -+ return rate; -+} -+ -+static int hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long parent_rate) -+{ -+ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); -+ -+ if (hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE) -+ hdptx_phy_disable(hdptx); -+ -+ rate = rate / 100; -+ -+ return hdptx_ropll_cmn_config(hdptx, rate); -+} -+ -+static int hdptx_phy_clk_enable(struct clk_hw *hw) -+{ -+ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); -+ int ret; -+ -+ if (hdptx->count) { -+ hdptx->count++; -+ return 0; -+ } -+ -+ ret = clk_bulk_enable(hdptx->nr_clks, hdptx->clks); -+ if (ret) { -+ dev_err(hdptx->dev, "failed to enable clocks\n"); -+ return ret; -+ } -+ -+ if (hdptx->rate) { -+ ret = hdptx_ropll_cmn_config(hdptx, hdptx->rate / 100); -+ if (ret < 0) { -+ dev_err(hdptx->dev, "hdmi phy pll init failed\n"); -+ return ret; -+ } -+ } -+ -+ hdptx->count++; -+ -+ return 0; -+} -+ -+static void hdptx_phy_clk_disable(struct clk_hw *hw) -+{ -+ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); -+ -+ if (hdptx->count > 1) { -+ hdptx->count--; -+ return; -+ } -+ -+ if (hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE) -+ hdptx_phy_disable(hdptx); -+ clk_bulk_disable(hdptx->nr_clks, hdptx->clks); -+ hdptx->count--; -+} -+ -+static const struct clk_ops hdptx_phy_clk_ops = { -+ .recalc_rate = hdptx_phy_clk_recalc_rate, -+ .round_rate = hdptx_phy_clk_round_rate, -+ .set_rate = hdptx_phy_clk_set_rate, -+ .enable = hdptx_phy_clk_enable, -+ .disable = hdptx_phy_clk_disable, -+}; -+ -+static int rockchip_hdptx_phy_clk_register(struct rockchip_hdptx_phy *hdptx) -+{ -+ struct device *dev = hdptx->dev; -+ struct device_node *np = dev->of_node; -+ struct device_node *clk_np; -+ struct platform_device *pdev; -+ struct clk_init_data init = {}; -+ struct clk *refclk; -+ const char *parent_name; -+ int ret; -+ -+ clk_np = of_get_child_by_name(np, "clk-port"); -+ if (!clk_np) -+ return 0; -+ -+ pdev = of_platform_device_create(clk_np, NULL, dev); -+ if (!pdev) -+ return 0; -+ -+ refclk = devm_clk_get(dev, "ref"); -+ if (IS_ERR(refclk)) { -+ dev_err(dev, "failed to get ref clock\n"); -+ return PTR_ERR(refclk); -+ } -+ -+ parent_name = __clk_get_name(refclk); -+ -+ init.parent_names = &parent_name; -+ init.num_parents = 1; -+ init.flags = CLK_GET_RATE_NOCACHE; -+ if (!hdptx->id) -+ init.name = "clk_hdmiphy_pixel0"; -+ else -+ init.name = "clk_hdmiphy_pixel1"; -+ init.ops = &hdptx_phy_clk_ops; -+ -+ /* optional override of the clock name */ -+ of_property_read_string(np, "clock-output-names", &init.name); -+ -+ hdptx->hw.init = &init; -+ -+ hdptx->dclk = devm_clk_register(&pdev->dev, &hdptx->hw); -+ if (IS_ERR(hdptx->dclk)) { -+ ret = PTR_ERR(hdptx->dclk); -+ dev_err(dev, "failed to register clock: %d\n", ret); -+ return ret; -+ } -+ -+ ret = of_clk_add_provider(clk_np, of_clk_src_simple_get, hdptx->dclk); -+ if (ret) { -+ dev_err(dev, "failed to register OF clock provider: %d\n", ret); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int rockchip_hdptx_phy_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct device_node *np = dev->of_node; -+ struct rockchip_hdptx_phy *hdptx; -+ struct phy_provider *phy_provider; -+ struct resource *res; -+ void __iomem *regs; -+ int ret; -+ -+ hdptx = devm_kzalloc(dev, sizeof(*hdptx), GFP_KERNEL); -+ if (!hdptx) -+ return -ENOMEM; -+ -+ hdptx->dev = dev; -+ -+ hdptx->id = of_alias_get_id(dev->of_node, "hdptxhdmi"); -+ if (hdptx->id < 0) -+ hdptx->id = 0; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ regs = devm_ioremap_resource(dev, res); -+ if (IS_ERR(regs)) -+ return PTR_ERR(regs); -+ -+ ret = devm_clk_bulk_get_all(dev, &hdptx->clks); -+ if (ret < 1) -+ return dev_err_probe(dev, ret, "failed to get clocks\n"); -+ -+ hdptx->nr_clks = ret; -+ -+ ret = clk_bulk_prepare(hdptx->nr_clks, hdptx->clks); -+ if (ret) { -+ dev_err(hdptx->dev, "failed to prepare clocks\n"); -+ return ret; -+ } -+ -+ hdptx->regmap = devm_regmap_init_mmio(dev, regs, -+ &rockchip_hdptx_phy_regmap_config); -+ if (IS_ERR(hdptx->regmap)) { -+ ret = PTR_ERR(hdptx->regmap); -+ dev_err(dev, "failed to init regmap: %d\n", ret); -+ goto err_regsmap; -+ } -+ -+ hdptx->phy_reset = devm_reset_control_get(dev, "phy"); -+ if (IS_ERR(hdptx->phy_reset)) { -+ ret = PTR_ERR(hdptx->phy_reset); -+ dev_err(dev, "failed to get phy reset: %d\n", ret); -+ goto err_regsmap; -+ } -+ -+ hdptx->apb_reset = devm_reset_control_get(dev, "apb"); -+ if (IS_ERR(hdptx->apb_reset)) { -+ ret = PTR_ERR(hdptx->apb_reset); -+ dev_err(dev, "failed to get apb reset: %d\n", ret); -+ goto err_regsmap; -+ } -+ -+ hdptx->init_reset = devm_reset_control_get(dev, "init"); -+ if (IS_ERR(hdptx->init_reset)) { -+ ret = PTR_ERR(hdptx->init_reset); -+ dev_err(dev, "failed to get init reset: %d\n", ret); -+ goto err_regsmap; -+ } -+ -+ hdptx->cmn_reset = devm_reset_control_get(dev, "cmn"); -+ if (IS_ERR(hdptx->cmn_reset)) { -+ ret = PTR_ERR(hdptx->cmn_reset); -+ dev_err(dev, "failed to get apb reset: %d\n", ret); -+ goto err_regsmap; -+ } -+ -+ hdptx->lane_reset = devm_reset_control_get(dev, "lane"); -+ if (IS_ERR(hdptx->lane_reset)) { -+ ret = PTR_ERR(hdptx->lane_reset); -+ dev_err(dev, "failed to get lane reset: %d\n", ret); -+ goto err_regsmap; -+ } -+ -+ hdptx->ropll_reset = devm_reset_control_get(dev, "ropll"); -+ if (IS_ERR(hdptx->ropll_reset)) { -+ ret = PTR_ERR(hdptx->ropll_reset); -+ dev_err(dev, "failed to get ropll reset: %d\n", ret); -+ goto err_regsmap; -+ } -+ -+ hdptx->lcpll_reset = devm_reset_control_get(dev, "lcpll"); -+ if (IS_ERR(hdptx->lcpll_reset)) { -+ ret = PTR_ERR(hdptx->lcpll_reset); -+ dev_err(dev, "failed to get lcpll reset: %d\n", ret); -+ goto err_regsmap; -+ } -+ -+ hdptx->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); -+ if (IS_ERR(hdptx->grf)) { -+ ret = PTR_ERR(hdptx->grf); -+ dev_err(hdptx->dev, "Unable to get rockchip,grf\n"); -+ goto err_regsmap; -+ } -+ -+ hdptx->phy = devm_phy_create(dev, NULL, &rockchip_hdptx_phy_ops); -+ if (IS_ERR(hdptx->phy)) { -+ dev_err(dev, "failed to create HDMI PHY\n"); -+ ret = PTR_ERR(hdptx->phy); -+ goto err_regsmap; -+ } -+ -+ phy_set_drvdata(hdptx->phy, hdptx); -+ phy_set_bus_width(hdptx->phy, 8); -+ -+ pm_runtime_enable(dev); -+ ret = devm_add_action_or_reset(dev, rockchip_hdptx_phy_runtime_disable, -+ hdptx); -+ if (ret) -+ goto err_regsmap; -+ -+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); -+ if (IS_ERR(phy_provider)) { -+ dev_err(dev, "failed to register PHY provider\n"); -+ ret = PTR_ERR(phy_provider); -+ goto err_regsmap; -+ } -+ -+ reset_control_deassert(hdptx->apb_reset); -+ reset_control_deassert(hdptx->cmn_reset); -+ reset_control_deassert(hdptx->init_reset); -+ -+ ret = rockchip_hdptx_phy_clk_register(hdptx); -+ if (ret) -+ goto err_regsmap; -+ -+ platform_set_drvdata(pdev, hdptx); -+ dev_info(dev, "hdptx phy init success\n"); -+ return 0; -+ -+err_regsmap: -+ clk_bulk_unprepare(hdptx->nr_clks, hdptx->clks); -+ return ret; -+} -+ -+static struct platform_driver rockchip_hdptx_phy_driver = { -+ .probe = rockchip_hdptx_phy_probe, -+ .driver = { -+ .name = "rockchip-hdptx-phy-hdmi", -+ .of_match_table = of_match_ptr(rockchip_hdptx_phy_of_match), -+ }, -+}; -+ -+module_platform_driver(rockchip_hdptx_phy_driver); -+ -+MODULE_DESCRIPTION("Samsung HDMI-DP Transmitter Combphy Driver"); -+MODULE_LICENSE("GPL v2"); --- -Armbian - -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Boris Brezillon -Date: Tue, 12 Jul 2022 09:11:57 +0200 -Subject: drm/rockchip: dw hdmi qp support for rk3588 - -Cherry picked from panthor-v3-rk3588-evb1 tree and made some -adjust to fit the upstream drm driver. - -Now we can get a 4KP60 output on rk3588 evb1 - -Signed-off-by: Algea Cao -Signed-off-by: Andy Yan ---- - drivers/gpu/drm/bridge/synopsys/Kconfig | 13 +- - drivers/gpu/drm/bridge/synopsys/Makefile | 7 +- - drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c | 10 +- - drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h | 4 +- - drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c | 9 +- - drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h | 54 + - drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 11 +- - drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-audio.h | 29 + - drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.c | 336 + - drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.h | 25 + - drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c | 251 + - drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 2984 +++++++++ - drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h | 831 +++ - drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 2009 ++++-- - drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 71 +- - drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 3108 +++++++++- - include/drm/bridge/dw_hdmi.h | 129 +- - 17 files changed, 9148 insertions(+), 733 deletions(-) - -diff --git a/drivers/gpu/drm/bridge/synopsys/Kconfig b/drivers/gpu/drm/bridge/synopsys/Kconfig -index 15fc182d05ef..18ac9677dc93 100644 ---- a/drivers/gpu/drm/bridge/synopsys/Kconfig -+++ b/drivers/gpu/drm/bridge/synopsys/Kconfig -@@ -1,9 +1,8 @@ - # SPDX-License-Identifier: GPL-2.0-only - config DRM_DW_HDMI - tristate -- select DRM_DISPLAY_HDMI_HELPER -- select DRM_DISPLAY_HELPER - select DRM_KMS_HELPER -+ select DRM_DISPLAY_HDMI_HELPER - select REGMAP_MMIO - select CEC_CORE if CEC_NOTIFIER - -@@ -27,16 +26,6 @@ config DRM_DW_HDMI_I2S_AUDIO - Support the I2S Audio interface which is part of the Synopsys - Designware HDMI block. - --config DRM_DW_HDMI_GP_AUDIO -- tristate "Synopsys Designware GP Audio interface" -- depends on DRM_DW_HDMI && SND -- select SND_PCM -- select SND_PCM_ELD -- select SND_PCM_IEC958 -- help -- Support the GP Audio interface which is part of the Synopsys -- Designware HDMI block. -- - config DRM_DW_HDMI_CEC - tristate "Synopsis Designware CEC interface" - depends on DRM_DW_HDMI -diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile -index ce715562e9e5..da937282da87 100644 ---- a/drivers/gpu/drm/bridge/synopsys/Makefile -+++ b/drivers/gpu/drm/bridge/synopsys/Makefile -@@ -1,8 +1,7 @@ - # SPDX-License-Identifier: GPL-2.0-only --obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o -+obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o dw-hdmi-qp.o - obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o --obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o --obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o --obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o -+obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o dw-hdmi-qp-i2s-audio.o -+obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o dw-hdmi-qp-cec.o - - obj-$(CONFIG_DRM_DW_MIPI_DSI) += dw-mipi-dsi.o -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c -index 67b8d17a722a..bc54b4282cb6 100644 ---- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c -@@ -320,17 +320,13 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream) - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_dw_hdmi *dw = substream->private_data; - void __iomem *base = dw->data.base; -- u8 *eld; - int ret; - - runtime->hw = dw_hdmi_hw; - -- eld = dw->data.get_eld(dw->data.hdmi); -- if (eld) { -- ret = snd_pcm_hw_constraint_eld(runtime, eld); -- if (ret < 0) -- return ret; -- } -+ ret = snd_pcm_hw_constraint_eld(runtime, dw->data.eld); -+ if (ret < 0) -+ return ret; - - ret = snd_pcm_limit_hw_rates(runtime); - if (ret < 0) -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h -index f72d27208ebe..cb07dc0da5a7 100644 ---- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h -@@ -9,15 +9,15 @@ struct dw_hdmi_audio_data { - void __iomem *base; - int irq; - struct dw_hdmi *hdmi; -- u8 *(*get_eld)(struct dw_hdmi *hdmi); -+ u8 *eld; - }; - - struct dw_hdmi_i2s_audio_data { - struct dw_hdmi *hdmi; -+ u8 *eld; - - void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); - u8 (*read)(struct dw_hdmi *hdmi, int offset); -- u8 *(*get_eld)(struct dw_hdmi *hdmi); - }; - - #endif -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c -index 673661160e54..b8675ba16d61 100644 ---- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c -@@ -12,6 +12,7 @@ - #include - - #include -+#include - - #include - #include -@@ -270,12 +271,16 @@ static int dw_hdmi_cec_probe(struct platform_device *pdev) - if (IS_ERR(cec->adap)) - return PTR_ERR(cec->adap); - -+ dw_hdmi_set_cec_adap(cec->hdmi, cec->adap); -+ - /* override the module pointer */ - cec->adap->owner = THIS_MODULE; - -- ret = devm_add_action_or_reset(&pdev->dev, dw_hdmi_cec_del, cec); -- if (ret) -+ ret = devm_add_action(&pdev->dev, dw_hdmi_cec_del, cec); -+ if (ret) { -+ cec_delete_adapter(cec->adap); - return ret; -+ } - - ret = devm_request_threaded_irq(&pdev->dev, cec->irq, - dw_hdmi_cec_hardirq, -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h -new file mode 100644 -index 000000000000..d138f91f3422 ---- /dev/null -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h -@@ -0,0 +1,54 @@ -+/* -+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd -+ * Author Huicong Xu -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef DW_HDMI_HDCP_H -+#define DW_HDMI_HDCP_H -+ -+#include -+ -+#define DW_HDCP_DRIVER_NAME "dw-hdmi-hdcp" -+#define HDCP_PRIVATE_KEY_SIZE 280 -+#define HDCP_KEY_SHA_SIZE 20 -+ -+struct hdcp_keys { -+ u8 KSV[8]; -+ u8 devicekey[HDCP_PRIVATE_KEY_SIZE]; -+ u8 sha1[HDCP_KEY_SHA_SIZE]; -+}; -+ -+struct dw_hdcp { -+ bool enable; -+ int retry_times; -+ int remaining_times; -+ char *seeds; -+ int invalidkey; -+ char *invalidkeys; -+ int hdcp2_enable; -+ int status; -+ u32 reg_io_width; -+ -+ struct miscdevice mdev; -+ struct hdcp_keys *keys; -+ struct device *dev; -+ struct dw_hdmi *hdmi; -+ void __iomem *regs; -+ -+ void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); -+ u8 (*read)(struct dw_hdmi *hdmi, int offset); -+ int (*hdcp_start)(struct dw_hdcp *hdcp); -+ int (*hdcp_stop)(struct dw_hdcp *hdcp); -+ void (*hdcp_isr)(struct dw_hdcp *hdcp, int hdcp_int); -+}; -+ -+#endif -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c -index 26c187d20d97..e92dce88c2c3 100644 ---- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c -@@ -135,15 +135,8 @@ static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, uint8_t *buf, - size_t len) - { - struct dw_hdmi_i2s_audio_data *audio = data; -- u8 *eld; -- -- eld = audio->get_eld(audio->hdmi); -- if (eld) -- memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len)); -- else -- /* Pass en empty ELD if connector not available */ -- memset(buf, 0, len); - -+ memcpy(buf, audio->eld, min_t(size_t, MAX_ELD_BYTES, len)); - return 0; - } - -@@ -177,7 +170,7 @@ static int dw_hdmi_i2s_hook_plugged_cb(struct device *dev, void *data, - return dw_hdmi_set_plugged_cb(hdmi, fn, codec_dev); - } - --static const struct hdmi_codec_ops dw_hdmi_i2s_ops = { -+static struct hdmi_codec_ops dw_hdmi_i2s_ops = { - .hw_params = dw_hdmi_i2s_hw_params, - .audio_startup = dw_hdmi_i2s_audio_startup, - .audio_shutdown = dw_hdmi_i2s_audio_shutdown, -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-audio.h -new file mode 100644 -index 000000000000..93f1a42954e7 ---- /dev/null -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-audio.h -@@ -0,0 +1,29 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd. -+ * Author: Sugar Zhang -+ */ -+ -+#ifndef DW_HDMI_QP_AUDIO_H -+#define DW_HDMI_QP_AUDIO_H -+ -+struct dw_hdmi_qp; -+ -+struct dw_hdmi_qp_audio_data { -+ phys_addr_t phys; -+ void __iomem *base; -+ int irq; -+ struct dw_hdmi_qp *hdmi; -+ u8 *eld; -+}; -+ -+struct dw_hdmi_qp_i2s_audio_data { -+ struct dw_hdmi_qp *hdmi; -+ u8 *eld; -+ -+ void (*write)(struct dw_hdmi_qp *hdmi, u32 val, int offset); -+ u32 (*read)(struct dw_hdmi_qp *hdmi, int offset); -+ void (*mod)(struct dw_hdmi_qp *hdmi, u32 val, u32 mask, u32 reg); -+}; -+ -+#endif -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.c -new file mode 100644 -index 000000000000..05bcfcaec665 ---- /dev/null -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.c -@@ -0,0 +1,336 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright (C) Rockchip Electronics Co.Ltd -+ * Author: -+ * Algea Cao -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+#include -+ -+#include "dw-hdmi-qp-cec.h" -+ -+enum { -+ CEC_TX_CONTROL = 0x1000, -+ CEC_CTRL_CLEAR = BIT(0), -+ CEC_CTRL_START = BIT(0), -+ -+ CEC_STAT_DONE = BIT(0), -+ CEC_STAT_NACK = BIT(1), -+ CEC_STAT_ARBLOST = BIT(2), -+ CEC_STAT_LINE_ERR = BIT(3), -+ CEC_STAT_RETRANS_FAIL = BIT(4), -+ CEC_STAT_DISCARD = BIT(5), -+ CEC_STAT_TX_BUSY = BIT(8), -+ CEC_STAT_RX_BUSY = BIT(9), -+ CEC_STAT_DRIVE_ERR = BIT(10), -+ CEC_STAT_EOM = BIT(11), -+ CEC_STAT_NOTIFY_ERR = BIT(12), -+ -+ CEC_CONFIG = 0x1008, -+ CEC_ADDR = 0x100c, -+ CEC_TX_CNT = 0x1020, -+ CEC_RX_CNT = 0x1040, -+ CEC_TX_DATA3_0 = 0x1024, -+ CEC_RX_DATA3_0 = 0x1044, -+ CEC_LOCK_CONTROL = 0x1054, -+ -+ CEC_INT_STATUS = 0x4000, -+ CEC_INT_MASK_N = 0x4004, -+ CEC_INT_CLEAR = 0x4008, -+}; -+ -+struct dw_hdmi_qp_cec { -+ struct dw_hdmi_qp *hdmi; -+ const struct dw_hdmi_qp_cec_ops *ops; -+ u32 addresses; -+ struct cec_adapter *adap; -+ struct cec_msg rx_msg; -+ unsigned int tx_status; -+ bool tx_done; -+ bool rx_done; -+ struct cec_notifier *notify; -+ int irq; -+}; -+ -+static void dw_hdmi_qp_write(struct dw_hdmi_qp_cec *cec, u32 val, int offset) -+{ -+ cec->ops->write(cec->hdmi, val, offset); -+} -+ -+static u32 dw_hdmi_qp_read(struct dw_hdmi_qp_cec *cec, int offset) -+{ -+ return cec->ops->read(cec->hdmi, offset); -+} -+ -+static int dw_hdmi_qp_cec_log_addr(struct cec_adapter *adap, u8 logical_addr) -+{ -+ struct dw_hdmi_qp_cec *cec = cec_get_drvdata(adap); -+ -+ if (logical_addr == CEC_LOG_ADDR_INVALID) -+ cec->addresses = 0; -+ else -+ cec->addresses |= BIT(logical_addr) | BIT(15); -+ -+ dw_hdmi_qp_write(cec, cec->addresses, CEC_ADDR); -+ -+ return 0; -+} -+ -+static int dw_hdmi_qp_cec_transmit(struct cec_adapter *adap, u8 attempts, -+ u32 signal_free_time, struct cec_msg *msg) -+{ -+ struct dw_hdmi_qp_cec *cec = cec_get_drvdata(adap); -+ unsigned int i; -+ u32 val; -+ -+ for (i = 0; i < msg->len; i++) { -+ if (!(i % 4)) -+ val = msg->msg[i]; -+ if ((i % 4) == 1) -+ val |= msg->msg[i] << 8; -+ if ((i % 4) == 2) -+ val |= msg->msg[i] << 16; -+ if ((i % 4) == 3) -+ val |= msg->msg[i] << 24; -+ -+ if (i == (msg->len - 1) || (i % 4) == 3) -+ dw_hdmi_qp_write(cec, val, CEC_TX_DATA3_0 + (i / 4) * 4); -+ } -+ -+ dw_hdmi_qp_write(cec, msg->len - 1, CEC_TX_CNT); -+ dw_hdmi_qp_write(cec, CEC_CTRL_START, CEC_TX_CONTROL); -+ -+ return 0; -+} -+ -+static irqreturn_t dw_hdmi_qp_cec_hardirq(int irq, void *data) -+{ -+ struct cec_adapter *adap = data; -+ struct dw_hdmi_qp_cec *cec = cec_get_drvdata(adap); -+ u32 stat = dw_hdmi_qp_read(cec, CEC_INT_STATUS); -+ irqreturn_t ret = IRQ_HANDLED; -+ -+ if (stat == 0) -+ return IRQ_NONE; -+ -+ dw_hdmi_qp_write(cec, stat, CEC_INT_CLEAR); -+ -+ if (stat & CEC_STAT_LINE_ERR) { -+ cec->tx_status = CEC_TX_STATUS_ERROR; -+ cec->tx_done = true; -+ ret = IRQ_WAKE_THREAD; -+ } else if (stat & CEC_STAT_DONE) { -+ cec->tx_status = CEC_TX_STATUS_OK; -+ cec->tx_done = true; -+ ret = IRQ_WAKE_THREAD; -+ } else if (stat & CEC_STAT_NACK) { -+ cec->tx_status = CEC_TX_STATUS_NACK; -+ cec->tx_done = true; -+ ret = IRQ_WAKE_THREAD; -+ } -+ -+ if (stat & CEC_STAT_EOM) { -+ unsigned int len, i, val; -+ -+ val = dw_hdmi_qp_read(cec, CEC_RX_CNT); -+ len = (val & 0xf) + 1; -+ -+ if (len > sizeof(cec->rx_msg.msg)) -+ len = sizeof(cec->rx_msg.msg); -+ -+ for (i = 0; i < 4; i++) { -+ val = dw_hdmi_qp_read(cec, CEC_RX_DATA3_0 + i); -+ cec->rx_msg.msg[i * 4] = val & 0xff; -+ cec->rx_msg.msg[i * 4 + 1] = (val >> 8) & 0xff; -+ cec->rx_msg.msg[i * 4 + 2] = (val >> 16) & 0xff; -+ cec->rx_msg.msg[i * 4 + 3] = (val >> 24) & 0xff; -+ } -+ -+ dw_hdmi_qp_write(cec, 1, CEC_LOCK_CONTROL); -+ -+ cec->rx_msg.len = len; -+ cec->rx_done = true; -+ -+ ret = IRQ_WAKE_THREAD; -+ } -+ -+ return ret; -+} -+ -+static irqreturn_t dw_hdmi_qp_cec_thread(int irq, void *data) -+{ -+ struct cec_adapter *adap = data; -+ struct dw_hdmi_qp_cec *cec = cec_get_drvdata(adap); -+ -+ if (cec->tx_done) { -+ cec->tx_done = false; -+ cec_transmit_attempt_done(adap, cec->tx_status); -+ } -+ if (cec->rx_done) { -+ cec->rx_done = false; -+ cec_received_msg(adap, &cec->rx_msg); -+ } -+ return IRQ_HANDLED; -+} -+ -+static int dw_hdmi_qp_cec_enable(struct cec_adapter *adap, bool enable) -+{ -+ struct dw_hdmi_qp_cec *cec = cec_get_drvdata(adap); -+ -+ if (!enable) { -+ dw_hdmi_qp_write(cec, 0, CEC_INT_MASK_N); -+ dw_hdmi_qp_write(cec, ~0, CEC_INT_CLEAR); -+ cec->ops->disable(cec->hdmi); -+ } else { -+ unsigned int irqs; -+ -+ cec->ops->enable(cec->hdmi); -+ -+ dw_hdmi_qp_write(cec, ~0, CEC_INT_CLEAR); -+ dw_hdmi_qp_write(cec, 1, CEC_LOCK_CONTROL); -+ -+ dw_hdmi_qp_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID); -+ -+ irqs = CEC_STAT_LINE_ERR | CEC_STAT_NACK | CEC_STAT_EOM | -+ CEC_STAT_DONE; -+ dw_hdmi_qp_write(cec, ~0, CEC_INT_CLEAR); -+ dw_hdmi_qp_write(cec, irqs, CEC_INT_MASK_N); -+ } -+ return 0; -+} -+ -+static const struct cec_adap_ops dw_hdmi_qp_cec_ops = { -+ .adap_enable = dw_hdmi_qp_cec_enable, -+ .adap_log_addr = dw_hdmi_qp_cec_log_addr, -+ .adap_transmit = dw_hdmi_qp_cec_transmit, -+}; -+ -+static void dw_hdmi_qp_cec_del(void *data) -+{ -+ struct dw_hdmi_qp_cec *cec = data; -+ -+ cec_delete_adapter(cec->adap); -+} -+ -+static int dw_hdmi_qp_cec_probe(struct platform_device *pdev) -+{ -+ struct dw_hdmi_qp_cec_data *data = dev_get_platdata(&pdev->dev); -+ struct dw_hdmi_qp_cec *cec; -+ int ret; -+ -+ if (!data) { -+ dev_err(&pdev->dev, "can't get data\n"); -+ return -ENXIO; -+ } -+ -+ /* -+ * Our device is just a convenience - we want to link to the real -+ * hardware device here, so that userspace can see the association -+ * between the HDMI hardware and its associated CEC chardev. -+ */ -+ cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL); -+ if (!cec) -+ return -ENOMEM; -+ -+ cec->ops = data->ops; -+ cec->hdmi = data->hdmi; -+ cec->irq = data->irq; -+ -+ platform_set_drvdata(pdev, cec); -+ -+ dw_hdmi_qp_write(cec, 0, CEC_TX_CNT); -+ dw_hdmi_qp_write(cec, ~0, CEC_INT_CLEAR); -+ dw_hdmi_qp_write(cec, 0, CEC_INT_MASK_N); -+ -+ cec->adap = cec_allocate_adapter(&dw_hdmi_qp_cec_ops, cec, "dw_hdmi_qp", -+ CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | -+ CEC_CAP_RC | CEC_CAP_PASSTHROUGH, -+ CEC_MAX_LOG_ADDRS); -+ if (IS_ERR(cec->adap)) { -+ dev_err(&pdev->dev, "cec allocate adapter failed\n"); -+ return PTR_ERR(cec->adap); -+ } -+ -+ dw_hdmi_qp_set_cec_adap(cec->hdmi, cec->adap); -+ -+ /* override the module pointer */ -+ cec->adap->owner = THIS_MODULE; -+ -+ ret = devm_add_action(&pdev->dev, dw_hdmi_qp_cec_del, cec); -+ if (ret) { -+ dev_err(&pdev->dev, "cec add action failed\n"); -+ cec_delete_adapter(cec->adap); -+ return ret; -+ } -+ -+ if (cec->irq < 0) { -+ ret = cec->irq; -+ dev_err(&pdev->dev, "cec get irq failed\n"); -+ return ret; -+ } -+ -+ ret = devm_request_threaded_irq(&pdev->dev, cec->irq, -+ dw_hdmi_qp_cec_hardirq, -+ dw_hdmi_qp_cec_thread, IRQF_SHARED, -+ "dw-hdmi-qp-cec", cec->adap); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "cec request irq thread failed\n"); -+ return ret; -+ } -+ -+ cec->notify = cec_notifier_cec_adap_register(pdev->dev.parent, -+ NULL, cec->adap); -+ if (!cec->notify) { -+ dev_err(&pdev->dev, "cec notifier adap register failed\n"); -+ return -ENOMEM; -+ } -+ -+ ret = cec_register_adapter(cec->adap, pdev->dev.parent); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "cec adap register failed\n"); -+ cec_notifier_cec_adap_unregister(cec->notify, cec->adap); -+ return ret; -+ } -+ -+ /* -+ * CEC documentation says we must not call cec_delete_adapter -+ * after a successful call to cec_register_adapter(). -+ */ -+ devm_remove_action(&pdev->dev, dw_hdmi_qp_cec_del, cec); -+ -+ return 0; -+} -+ -+static int dw_hdmi_qp_cec_remove(struct platform_device *pdev) -+{ -+ struct dw_hdmi_qp_cec *cec = platform_get_drvdata(pdev); -+ -+ cec_notifier_cec_adap_unregister(cec->notify, cec->adap); -+ cec_unregister_adapter(cec->adap); -+ -+ return 0; -+} -+ -+static struct platform_driver dw_hdmi_qp_cec_driver = { -+ .probe = dw_hdmi_qp_cec_probe, -+ .remove = dw_hdmi_qp_cec_remove, -+ .driver = { -+ .name = "dw-hdmi-qp-cec", -+ }, -+}; -+module_platform_driver(dw_hdmi_qp_cec_driver); -+ -+MODULE_AUTHOR("Algea Cao "); -+MODULE_DESCRIPTION("Synopsys Designware HDMI QP CEC driver"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-qp-cec"); -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.h -new file mode 100644 -index 000000000000..c0977c612e34 ---- /dev/null -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.h -@@ -0,0 +1,25 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (C) Rockchip Electronics Co.Ltd -+ * Author: -+ * Algea Cao -+ */ -+#ifndef DW_HDMI_QP_CEC_H -+#define DW_HDMI_QP_CEC_H -+ -+struct dw_hdmi_qp; -+ -+struct dw_hdmi_qp_cec_ops { -+ void (*enable)(struct dw_hdmi_qp *hdmi); -+ void (*disable)(struct dw_hdmi_qp *hdmi); -+ void (*write)(struct dw_hdmi_qp *hdmi, u32 val, int offset); -+ u32 (*read)(struct dw_hdmi_qp *hdmi, int offset); -+}; -+ -+struct dw_hdmi_qp_cec_data { -+ struct dw_hdmi_qp *hdmi; -+ const struct dw_hdmi_qp_cec_ops *ops; -+ int irq; -+}; -+ -+#endif -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c -new file mode 100644 -index 000000000000..40725f237cd6 ---- /dev/null -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c -@@ -0,0 +1,251 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * dw-hdmi-qp-i2s-audio.c -+ * -+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd. -+ * Author: Sugar Zhang -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include "dw-hdmi-qp.h" -+#include "dw-hdmi-qp-audio.h" -+ -+#define DRIVER_NAME "dw-hdmi-qp-i2s-audio" -+ -+static inline void hdmi_write(struct dw_hdmi_qp_i2s_audio_data *audio, -+ u32 val, int offset) -+{ -+ struct dw_hdmi_qp *hdmi = audio->hdmi; -+ -+ audio->write(hdmi, val, offset); -+} -+ -+static inline u32 hdmi_read(struct dw_hdmi_qp_i2s_audio_data *audio, int offset) -+{ -+ struct dw_hdmi_qp *hdmi = audio->hdmi; -+ -+ return audio->read(hdmi, offset); -+} -+ -+static inline void hdmi_mod(struct dw_hdmi_qp_i2s_audio_data *audio, -+ u32 data, u32 mask, u32 reg) -+{ -+ struct dw_hdmi_qp *hdmi = audio->hdmi; -+ -+ return audio->mod(hdmi, data, mask, reg); -+} -+ -+static inline bool is_dw_hdmi_qp_clk_off(struct dw_hdmi_qp_i2s_audio_data *audio) -+{ -+ u32 sta = hdmi_read(audio, CMU_STATUS); -+ -+ return (sta & (AUDCLK_OFF | LINKQPCLK_OFF | VIDQPCLK_OFF)); -+} -+ -+static int dw_hdmi_qp_i2s_hw_params(struct device *dev, void *data, -+ struct hdmi_codec_daifmt *fmt, -+ struct hdmi_codec_params *hparms) -+{ -+ struct dw_hdmi_qp_i2s_audio_data *audio = data; -+ struct dw_hdmi_qp *hdmi = audio->hdmi; -+ u32 conf0 = 0; -+ bool ref2stream = false; -+ -+ if (is_dw_hdmi_qp_clk_off(audio)) -+ return 0; -+ -+ if (fmt->bit_clk_provider | fmt->frame_clk_provider) { -+ dev_err(dev, "unsupported clock settings\n"); -+ return -EINVAL; -+ } -+ -+ /* Reset the audio data path of the AVP */ -+ hdmi_write(audio, AVP_DATAPATH_PACKET_AUDIO_SWINIT_P, GLOBAL_SWRESET_REQUEST); -+ -+ /* Clear the audio FIFO */ -+ hdmi_write(audio, AUDIO_FIFO_CLR_P, AUDIO_INTERFACE_CONTROL0); -+ -+ /* Select I2S interface as the audio source */ -+ hdmi_mod(audio, AUD_IF_I2S, AUD_IF_SEL_MSK, AUDIO_INTERFACE_CONFIG0); -+ -+ /* Enable the active i2s lanes */ -+ switch (hparms->channels) { -+ case 7 ... 8: -+ conf0 |= I2S_LINES_EN(3); -+ fallthrough; -+ case 5 ... 6: -+ conf0 |= I2S_LINES_EN(2); -+ fallthrough; -+ case 3 ... 4: -+ conf0 |= I2S_LINES_EN(1); -+ fallthrough; -+ default: -+ conf0 |= I2S_LINES_EN(0); -+ break; -+ } -+ -+ hdmi_mod(audio, conf0, I2S_LINES_EN_MSK, AUDIO_INTERFACE_CONFIG0); -+ -+ /* -+ * Enable bpcuv generated internally for L-PCM, or received -+ * from stream for NLPCM/HBR. -+ */ -+ switch (fmt->bit_fmt) { -+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: -+ conf0 = (hparms->channels == 8) ? AUD_HBR : AUD_ASP; -+ conf0 |= I2S_BPCUV_RCV_EN; -+ ref2stream = true; -+ break; -+ default: -+ conf0 = AUD_ASP | I2S_BPCUV_RCV_DIS; -+ ref2stream = false; -+ break; -+ } -+ -+ hdmi_mod(audio, conf0, I2S_BPCUV_RCV_MSK | AUD_FORMAT_MSK, -+ AUDIO_INTERFACE_CONFIG0); -+ -+ /* Enable audio FIFO auto clear when overflow */ -+ hdmi_mod(audio, AUD_FIFO_INIT_ON_OVF_EN, AUD_FIFO_INIT_ON_OVF_MSK, -+ AUDIO_INTERFACE_CONFIG0); -+ -+ dw_hdmi_qp_set_sample_rate(hdmi, hparms->sample_rate); -+ dw_hdmi_qp_set_channel_status(hdmi, hparms->iec.status, ref2stream); -+ dw_hdmi_qp_set_channel_count(hdmi, hparms->channels); -+ dw_hdmi_qp_set_channel_allocation(hdmi, hparms->cea.channel_allocation); -+ dw_hdmi_qp_set_audio_infoframe(hdmi, hparms); -+ -+ return 0; -+} -+ -+static int dw_hdmi_qp_i2s_audio_startup(struct device *dev, void *data) -+{ -+ struct dw_hdmi_qp_i2s_audio_data *audio = data; -+ struct dw_hdmi_qp *hdmi = audio->hdmi; -+ -+ if (is_dw_hdmi_qp_clk_off(audio)) -+ return 0; -+ -+ dw_hdmi_qp_audio_enable(hdmi); -+ -+ return 0; -+} -+ -+static void dw_hdmi_qp_i2s_audio_shutdown(struct device *dev, void *data) -+{ -+ struct dw_hdmi_qp_i2s_audio_data *audio = data; -+ struct dw_hdmi_qp *hdmi = audio->hdmi; -+ -+ if (is_dw_hdmi_qp_clk_off(audio)) -+ return; -+ -+ dw_hdmi_qp_audio_disable(hdmi); -+} -+ -+static int dw_hdmi_qp_i2s_get_eld(struct device *dev, void *data, uint8_t *buf, -+ size_t len) -+{ -+ struct dw_hdmi_qp_i2s_audio_data *audio = data; -+ -+ memcpy(buf, audio->eld, min_t(size_t, MAX_ELD_BYTES, len)); -+ -+ return 0; -+} -+ -+static int dw_hdmi_qp_i2s_get_dai_id(struct snd_soc_component *component, -+ struct device_node *endpoint) -+{ -+ struct of_endpoint of_ep; -+ int ret; -+ -+ ret = of_graph_parse_endpoint(endpoint, &of_ep); -+ if (ret < 0) -+ return ret; -+ -+ /* -+ * HDMI sound should be located as reg = <2> -+ * Then, it is sound port 0 -+ */ -+ if (of_ep.port == 2) -+ return 0; -+ -+ return -EINVAL; -+} -+ -+static int dw_hdmi_qp_i2s_hook_plugged_cb(struct device *dev, void *data, -+ hdmi_codec_plugged_cb fn, -+ struct device *codec_dev) -+{ -+ struct dw_hdmi_qp_i2s_audio_data *audio = data; -+ struct dw_hdmi_qp *hdmi = audio->hdmi; -+ -+ return dw_hdmi_qp_set_plugged_cb(hdmi, fn, codec_dev); -+} -+ -+static struct hdmi_codec_ops dw_hdmi_qp_i2s_ops = { -+ .hw_params = dw_hdmi_qp_i2s_hw_params, -+ .audio_startup = dw_hdmi_qp_i2s_audio_startup, -+ .audio_shutdown = dw_hdmi_qp_i2s_audio_shutdown, -+ .get_eld = dw_hdmi_qp_i2s_get_eld, -+ .get_dai_id = dw_hdmi_qp_i2s_get_dai_id, -+ .hook_plugged_cb = dw_hdmi_qp_i2s_hook_plugged_cb, -+}; -+ -+static int snd_dw_hdmi_qp_probe(struct platform_device *pdev) -+{ -+ struct dw_hdmi_qp_i2s_audio_data *audio = pdev->dev.platform_data; -+ struct platform_device_info pdevinfo; -+ struct hdmi_codec_pdata pdata; -+ struct platform_device *platform; -+ -+ pdata.ops = &dw_hdmi_qp_i2s_ops; -+ pdata.i2s = 1; -+ pdata.max_i2s_channels = 8; -+ pdata.data = audio; -+ -+ memset(&pdevinfo, 0, sizeof(pdevinfo)); -+ pdevinfo.parent = pdev->dev.parent; -+ pdevinfo.id = PLATFORM_DEVID_AUTO; -+ pdevinfo.name = HDMI_CODEC_DRV_NAME; -+ pdevinfo.data = &pdata; -+ pdevinfo.size_data = sizeof(pdata); -+ pdevinfo.dma_mask = DMA_BIT_MASK(32); -+ -+ platform = platform_device_register_full(&pdevinfo); -+ if (IS_ERR(platform)) -+ return PTR_ERR(platform); -+ -+ dev_set_drvdata(&pdev->dev, platform); -+ -+ return 0; -+} -+ -+static int snd_dw_hdmi_qp_remove(struct platform_device *pdev) -+{ -+ struct platform_device *platform = dev_get_drvdata(&pdev->dev); -+ -+ platform_device_unregister(platform); -+ -+ return 0; -+} -+ -+static struct platform_driver snd_dw_hdmi_qp_driver = { -+ .probe = snd_dw_hdmi_qp_probe, -+ .remove = snd_dw_hdmi_qp_remove, -+ .driver = { -+ .name = DRIVER_NAME, -+ }, -+}; -+module_platform_driver(snd_dw_hdmi_qp_driver); -+ -+MODULE_AUTHOR("Sugar Zhang "); -+MODULE_DESCRIPTION("Synopsis Designware HDMI QP I2S ALSA SoC interface"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:" DRIVER_NAME); -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c -new file mode 100644 -index 000000000000..10dfccae8455 ---- /dev/null -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c -@@ -0,0 +1,2984 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright (C) Rockchip Electronics Co.Ltd -+ * Author: -+ * Algea Cao -+ */ -+#define DEBUG -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "dw-hdmi-qp-audio.h" -+#include "dw-hdmi-qp.h" -+#include "dw-hdmi-qp-cec.h" -+ -+#include -+ -+#define DDC_CI_ADDR 0x37 -+#define DDC_SEGMENT_ADDR 0x30 -+ -+#define HDMI_EDID_LEN 512 -+ -+/* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */ -+#define SCDC_MIN_SOURCE_VERSION 0x1 -+ -+#define HDMI14_MAX_TMDSCLK 340000000 -+#define HDMI20_MAX_TMDSCLK_KHZ 600000 -+ -+static const unsigned int dw_hdmi_cable[] = { -+ EXTCON_DISP_HDMI, -+ EXTCON_NONE, -+}; -+ -+/* -+ * Unless otherwise noted, entries in this table are 100% optimization. -+ * Values can be obtained from hdmi_compute_n() but that function is -+ * slow so we pre-compute values we expect to see. -+ * -+ * All 32k and 48k values are expected to be the same (due to the way -+ * the math works) for any rate that's an exact kHz. -+ */ -+static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = { -+ { .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, }, -+ { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, }, -+ { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, -+ { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, }, -+ { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, }, -+ { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, -+ { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, -+ { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, -+ { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, -+ { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, -+ { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, -+ { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, -+ { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, -+ { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, -+ { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, }, -+ { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, -+ { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, }, -+ { .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, -+ { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, -+ { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, }, -+ { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, -+ { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, -+ { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, -+ { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, -+ { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, -+ { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, -+ { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, -+ { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, -+ { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, -+ { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, -+ { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, }, -+ { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, -+ { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, -+ { .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, -+ { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, -+ { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, -+ { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, -+ -+ /* For 297 MHz+ HDMI spec have some other rule for setting N */ -+ { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, }, -+ { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, }, -+ -+ /* End of table */ -+ { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, }, -+}; -+ -+static const struct drm_display_mode dw_hdmi_default_modes[] = { -+ /* 16 - 1920x1080@60Hz 16:9 */ -+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, -+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, -+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, -+ /* 2 - 720x480@60Hz 4:3 */ -+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, -+ 798, 858, 0, 480, 489, 495, 525, 0, -+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, -+ /* 4 - 1280x720@60Hz 16:9 */ -+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, -+ 1430, 1650, 0, 720, 725, 730, 750, 0, -+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, -+ /* 31 - 1920x1080@50Hz 16:9 */ -+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, -+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, -+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, -+ /* 19 - 1280x720@50Hz 16:9 */ -+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, -+ 1760, 1980, 0, 720, 725, 730, 750, 0, -+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, -+ /* 17 - 720x576@50Hz 4:3 */ -+ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, -+ 796, 864, 0, 576, 581, 586, 625, 0, -+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, -+ /* 2 - 720x480@60Hz 4:3 */ -+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, -+ 798, 858, 0, 480, 489, 495, 525, 0, -+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, -+}; -+ -+enum frl_mask { -+ FRL_3GBPS_3LANE = 1, -+ FRL_6GBPS_3LANE, -+ FRL_6GBPS_4LANE, -+ FRL_8GBPS_4LANE, -+ FRL_10GBPS_4LANE, -+ FRL_12GBPS_4LANE, -+}; -+ -+struct hdmi_vmode_qp { -+ bool mdataenablepolarity; -+ -+ unsigned int previous_pixelclock; -+ unsigned long mpixelclock; -+ unsigned int mpixelrepetitioninput; -+ unsigned int mpixelrepetitionoutput; -+ unsigned long previous_tmdsclock; -+ unsigned int mtmdsclock; -+}; -+ -+struct hdmi_qp_data_info { -+ unsigned int enc_in_bus_format; -+ unsigned int enc_out_bus_format; -+ unsigned int enc_in_encoding; -+ unsigned int enc_out_encoding; -+ unsigned int quant_range; -+ unsigned int pix_repet_factor; -+ struct hdmi_vmode_qp video_mode; -+ bool update; -+}; -+ -+struct dw_hdmi_qp_i2c { -+ struct i2c_adapter adap; -+ -+ struct mutex lock; /* used to serialize data transfers */ -+ struct completion cmp; -+ u32 stat; -+ -+ u8 slave_reg; -+ bool is_regaddr; -+ bool is_segment; -+ -+ unsigned int scl_high_ns; -+ unsigned int scl_low_ns; -+}; -+ -+struct dw_hdmi_phy_data { -+ enum dw_hdmi_phy_type type; -+ const char *name; -+ unsigned int gen; -+ bool has_svsret; -+ int (*configure)(struct dw_hdmi_qp *hdmi, -+ const struct dw_hdmi_plat_data *pdata, -+ unsigned long mpixelclock); -+}; -+ -+struct dw_hdmi_qp { -+ struct drm_connector connector; -+ struct drm_bridge bridge; -+ struct platform_device *hdcp_dev; -+ struct platform_device *audio; -+ struct platform_device *cec; -+ struct device *dev; -+ struct dw_hdmi_qp_i2c *i2c; -+ -+ struct hdmi_qp_data_info hdmi_data; -+ const struct dw_hdmi_plat_data *plat_data; -+ -+ int vic; -+ int main_irq; -+ int avp_irq; -+ int earc_irq; -+ -+ u8 edid[HDMI_EDID_LEN]; -+ -+ struct { -+ const struct dw_hdmi_qp_phy_ops *ops; -+ const char *name; -+ void *data; -+ bool enabled; -+ } phy; -+ -+ struct drm_display_mode previous_mode; -+ -+ struct i2c_adapter *ddc; -+ void __iomem *regs; -+ bool sink_is_hdmi; -+ bool sink_has_audio; -+ bool dclk_en; -+ -+ struct mutex mutex; /* for state below and previous_mode */ -+ struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */ -+ enum drm_connector_force force; /* mutex-protected force state */ -+ bool disabled; /* DRM has disabled our bridge */ -+ bool bridge_is_on; /* indicates the bridge is on */ -+ bool rxsense; /* rxsense state */ -+ u8 phy_mask; /* desired phy int mask settings */ -+ u8 mc_clkdis; /* clock disable register */ -+ -+ u32 scdc_intr; -+ u32 flt_intr; -+ u32 earc_intr; -+ -+ struct mutex audio_mutex; -+ unsigned int sample_rate; -+ unsigned int audio_cts; -+ unsigned int audio_n; -+ bool audio_enable; -+ void (*enable_audio)(struct dw_hdmi_qp *hdmi); -+ void (*disable_audio)(struct dw_hdmi_qp *hdmi); -+ -+ struct dentry *debugfs_dir; -+ bool scramble_low_rates; -+ -+ struct extcon_dev *extcon; -+ -+ struct regmap *regm; -+ -+ bool initialized; /* hdmi is enabled before bind */ -+ struct completion flt_cmp; -+ struct completion earc_cmp; -+ -+ struct cec_notifier *cec_notifier; -+ struct cec_adapter *cec_adap; -+ struct mutex cec_notifier_mutex; -+ -+ hdmi_codec_plugged_cb plugged_cb; -+ struct device *codec_dev; -+ enum drm_connector_status last_connector_result; -+}; -+ -+static inline void hdmi_writel(struct dw_hdmi_qp *hdmi, u32 val, int offset) -+{ -+ regmap_write(hdmi->regm, offset, val); -+} -+ -+static inline u32 hdmi_readl(struct dw_hdmi_qp *hdmi, int offset) -+{ -+ unsigned int val = 0; -+ -+ regmap_read(hdmi->regm, offset, &val); -+ -+ return val; -+} -+ -+static void handle_plugged_change(struct dw_hdmi_qp *hdmi, bool plugged) -+{ -+ if (hdmi->plugged_cb && hdmi->codec_dev) -+ hdmi->plugged_cb(hdmi->codec_dev, plugged); -+} -+ -+int dw_hdmi_qp_set_plugged_cb(struct dw_hdmi_qp *hdmi, hdmi_codec_plugged_cb fn, -+ struct device *codec_dev) -+{ -+ bool plugged; -+ -+ mutex_lock(&hdmi->mutex); -+ hdmi->plugged_cb = fn; -+ hdmi->codec_dev = codec_dev; -+ plugged = hdmi->last_connector_result == connector_status_connected; -+ handle_plugged_change(hdmi, plugged); -+ mutex_unlock(&hdmi->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_plugged_cb); -+ -+static void hdmi_modb(struct dw_hdmi_qp *hdmi, u32 data, u32 mask, u32 reg) -+{ -+ regmap_update_bits(hdmi->regm, reg, mask, data); -+} -+ -+static void hdmi_set_cts_n(struct dw_hdmi_qp *hdmi, unsigned int cts, -+ unsigned int n) -+{ -+ /* Set N */ -+ hdmi_modb(hdmi, n, AUDPKT_ACR_N_VALUE, AUDPKT_ACR_CONTROL0); -+ -+ /* Set CTS */ -+ if (cts) -+ hdmi_modb(hdmi, AUDPKT_ACR_CTS_OVR_EN, AUDPKT_ACR_CTS_OVR_EN_MSK, -+ AUDPKT_ACR_CONTROL1); -+ else -+ hdmi_modb(hdmi, 0, AUDPKT_ACR_CTS_OVR_EN_MSK, -+ AUDPKT_ACR_CONTROL1); -+ -+ hdmi_modb(hdmi, AUDPKT_ACR_CTS_OVR_VAL(cts), AUDPKT_ACR_CTS_OVR_VAL_MSK, -+ AUDPKT_ACR_CONTROL1); -+} -+ -+static int hdmi_match_tmds_n_table(struct dw_hdmi_qp *hdmi, -+ unsigned long pixel_clk, -+ unsigned long freq) -+{ -+ const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data; -+ const struct dw_hdmi_audio_tmds_n *tmds_n = NULL; -+ int i; -+ -+ if (plat_data->tmds_n_table) { -+ for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) { -+ if (pixel_clk == plat_data->tmds_n_table[i].tmds) { -+ tmds_n = &plat_data->tmds_n_table[i]; -+ break; -+ } -+ } -+ } -+ -+ if (tmds_n == NULL) { -+ for (i = 0; common_tmds_n_table[i].tmds != 0; i++) { -+ if (pixel_clk == common_tmds_n_table[i].tmds) { -+ tmds_n = &common_tmds_n_table[i]; -+ break; -+ } -+ } -+ } -+ -+ if (tmds_n == NULL) -+ return -ENOENT; -+ -+ switch (freq) { -+ case 32000: -+ return tmds_n->n_32k; -+ case 44100: -+ case 88200: -+ case 176400: -+ return (freq / 44100) * tmds_n->n_44k1; -+ case 48000: -+ case 96000: -+ case 192000: -+ return (freq / 48000) * tmds_n->n_48k; -+ default: -+ return -ENOENT; -+ } -+} -+ -+static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n, -+ unsigned int pixel_clk) -+{ -+ u64 final, diff; -+ u64 cts; -+ -+ final = (u64)pixel_clk * n; -+ -+ cts = final; -+ do_div(cts, 128 * freq); -+ -+ diff = final - (u64)cts * (128 * freq); -+ -+ return diff; -+} -+ -+static unsigned int hdmi_compute_n(struct dw_hdmi_qp *hdmi, -+ unsigned long pixel_clk, -+ unsigned long freq) -+{ -+ unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500); -+ unsigned int max_n = (128 * freq) / 300; -+ unsigned int ideal_n = (128 * freq) / 1000; -+ unsigned int best_n_distance = ideal_n; -+ unsigned int best_n = 0; -+ u64 best_diff = U64_MAX; -+ int n; -+ -+ /* If the ideal N could satisfy the audio math, then just take it */ -+ if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0) -+ return ideal_n; -+ -+ for (n = min_n; n <= max_n; n++) { -+ u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk); -+ -+ if (diff < best_diff || (diff == best_diff && -+ abs(n - ideal_n) < best_n_distance)) { -+ best_n = n; -+ best_diff = diff; -+ best_n_distance = abs(best_n - ideal_n); -+ } -+ -+ /* -+ * The best N already satisfy the audio math, and also be -+ * the closest value to ideal N, so just cut the loop. -+ */ -+ if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance)) -+ break; -+ } -+ -+ return best_n; -+} -+ -+static unsigned int hdmi_find_n(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk, -+ unsigned long sample_rate) -+{ -+ int n; -+ -+ n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate); -+ if (n > 0) -+ return n; -+ -+ dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n", -+ pixel_clk); -+ -+ return hdmi_compute_n(hdmi, pixel_clk, sample_rate); -+} -+ -+/* -+ * When transmitting IEC60958 linear PCM audio, these registers allow to -+ * configure the channel status information of all the channel status -+ * bits in the IEC60958 frame. For the moment this configuration is only -+ * used when the I2S audio interface, General Purpose Audio (GPA), -+ * or AHB audio DMA (AHBAUDDMA) interface is active -+ * (for S/PDIF interface this information comes from the stream). -+ */ -+void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, -+ u8 *channel_status, bool ref2stream) -+{ -+ mutex_lock(&hdmi->audio_mutex); -+ if (!hdmi->dclk_en) { -+ mutex_unlock(&hdmi->audio_mutex); -+ return; -+ } -+ -+ /* Set channel status */ -+ hdmi_writel(hdmi, channel_status[3] | (channel_status[4] << 8), -+ AUDPKT_CHSTATUS_OVR1); -+ -+ if (ref2stream) -+ hdmi_modb(hdmi, 0, -+ AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, -+ AUDPKT_CONTROL0); -+ else -+ hdmi_modb(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN, -+ AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, -+ AUDPKT_CONTROL0); -+ -+ mutex_unlock(&hdmi->audio_mutex); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_status); -+ -+static void hdmi_set_clk_regenerator(struct dw_hdmi_qp *hdmi, -+ unsigned long pixel_clk, unsigned int sample_rate) -+{ -+ unsigned int n = 0, cts = 0; -+ -+ n = hdmi_find_n(hdmi, pixel_clk, sample_rate); -+ -+ hdmi->audio_n = n; -+ hdmi->audio_cts = cts; -+ hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0); -+} -+ -+static void hdmi_init_clk_regenerator(struct dw_hdmi_qp *hdmi) -+{ -+ mutex_lock(&hdmi->audio_mutex); -+ if (hdmi->dclk_en) -+ hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate); -+ mutex_unlock(&hdmi->audio_mutex); -+} -+ -+static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi_qp *hdmi) -+{ -+ mutex_lock(&hdmi->audio_mutex); -+ if (hdmi->dclk_en) -+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, -+ hdmi->sample_rate); -+ mutex_unlock(&hdmi->audio_mutex); -+} -+ -+void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned int rate) -+{ -+ mutex_lock(&hdmi->audio_mutex); -+ if (hdmi->dclk_en) { -+ hdmi->sample_rate = rate; -+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, -+ hdmi->sample_rate); -+ } -+ mutex_unlock(&hdmi->audio_mutex); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_sample_rate); -+ -+void dw_hdmi_qp_set_channel_count(struct dw_hdmi_qp *hdmi, unsigned int cnt) -+{ -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_count); -+ -+void dw_hdmi_qp_set_channel_allocation(struct dw_hdmi_qp *hdmi, unsigned int ca) -+{ -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_allocation); -+ -+void dw_hdmi_qp_set_audio_infoframe(struct dw_hdmi_qp *hdmi, -+ struct hdmi_codec_params *hparms) -+{ -+ u8 infoframe_buf[HDMI_INFOFRAME_SIZE(AUDIO)]; -+ int ret = 0; -+ -+ ret = hdmi_audio_infoframe_pack(&hparms->cea, infoframe_buf, -+ sizeof(infoframe_buf)); -+ if (!ret) { -+ dev_err(hdmi->dev, "%s: Failed to pack audio infoframe: %d\n", -+ __func__, ret); -+ return; -+ } -+ -+ mutex_lock(&hdmi->audio_mutex); -+ if (!hdmi->dclk_en) { -+ mutex_unlock(&hdmi->audio_mutex); -+ return; -+ } -+ -+ /* -+ * AUDI_CONTENTS0: { RSV, HB2, HB1, RSV } -+ * AUDI_CONTENTS1: { PB3, PB2, PB1, PB0 } -+ * AUDI_CONTENTS2: { PB7, PB6, PB5, PB4 } -+ * -+ * PB0: CheckSum -+ * PB1: | CT3 | CT2 | CT1 | CT0 | F13 | CC2 | CC1 | CC0 | -+ * PB2: | F27 | F26 | F25 | SF2 | SF1 | SF0 | SS1 | SS0 | -+ * PB3: | F37 | F36 | F35 | F34 | F33 | F32 | F31 | F30 | -+ * PB4: | CA7 | CA6 | CA5 | CA4 | CA3 | CA2 | CA1 | CA0 | -+ * PB5: | DM_INH | LSV3 | LSV2 | LSV1 | LSV0 | F52 | F51 | F50 | -+ * PB6~PB10: Reserved -+ * -+ * AUDI_CONTENTS0 default value defined by HDMI specification, -+ * and shall only be changed for debug purposes. -+ * So, we only configure payload byte from PB0~PB7(2 word total). -+ */ -+ regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS1, &infoframe_buf[3], 2); -+ -+ /* Enable ACR, AUDI, AMD */ -+ hdmi_modb(hdmi, -+ PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, -+ PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, -+ PKTSCHED_PKT_EN); -+ -+ /* Enable AUDS */ -+ hdmi_modb(hdmi, PKTSCHED_AUDS_TX_EN, PKTSCHED_AUDS_TX_EN, PKTSCHED_PKT_EN); -+ mutex_unlock(&hdmi->audio_mutex); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_audio_infoframe); -+ -+static void hdmi_enable_audio_clk(struct dw_hdmi_qp *hdmi, bool enable) -+{ -+ if (enable) -+ hdmi_modb(hdmi, 0, -+ AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE); -+ else -+ hdmi_modb(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, -+ AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE); -+} -+ -+static void dw_hdmi_i2s_audio_enable(struct dw_hdmi_qp *hdmi) -+{ -+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); -+ hdmi_enable_audio_clk(hdmi, true); -+} -+ -+static void dw_hdmi_i2s_audio_disable(struct dw_hdmi_qp *hdmi) -+{ -+ /* Disable AUDS, ACR, AUDI, AMD */ -+ hdmi_modb(hdmi, 0, -+ PKTSCHED_ACR_TX_EN | PKTSCHED_AUDS_TX_EN | -+ PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, -+ PKTSCHED_PKT_EN); -+ -+ hdmi_enable_audio_clk(hdmi, false); -+} -+ -+void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi) -+{ -+ mutex_lock(&hdmi->audio_mutex); -+ if (hdmi->dclk_en) { -+ hdmi->audio_enable = true; -+ if (hdmi->enable_audio) -+ hdmi->enable_audio(hdmi); -+ } -+ mutex_unlock(&hdmi->audio_mutex); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_audio_enable); -+ -+void dw_hdmi_qp_audio_disable(struct dw_hdmi_qp *hdmi) -+{ -+ mutex_lock(&hdmi->audio_mutex); -+ if (hdmi->dclk_en) { -+ hdmi->audio_enable = false; -+ if (hdmi->disable_audio) -+ hdmi->disable_audio(hdmi); -+ } -+ mutex_unlock(&hdmi->audio_mutex); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_audio_disable); -+ -+static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) -+{ -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ case MEDIA_BUS_FMT_RGB101010_1X30: -+ case MEDIA_BUS_FMT_RGB121212_1X36: -+ case MEDIA_BUS_FMT_RGB161616_1X48: -+ return true; -+ -+ default: -+ return false; -+ } -+} -+ -+static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) -+{ -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_YUV8_1X24: -+ case MEDIA_BUS_FMT_YUV10_1X30: -+ case MEDIA_BUS_FMT_YUV12_1X36: -+ case MEDIA_BUS_FMT_YUV16_1X48: -+ return true; -+ -+ default: -+ return false; -+ } -+} -+ -+static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) -+{ -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_UYVY10_1X20: -+ case MEDIA_BUS_FMT_UYVY12_1X24: -+ return true; -+ -+ default: -+ return false; -+ } -+} -+ -+static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) -+{ -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: -+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: -+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: -+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: -+ return true; -+ -+ default: -+ return false; -+ } -+} -+ -+static int hdmi_bus_fmt_color_depth(unsigned int bus_format) -+{ -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ case MEDIA_BUS_FMT_YUV8_1X24: -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: -+ return 8; -+ -+ case MEDIA_BUS_FMT_RGB101010_1X30: -+ case MEDIA_BUS_FMT_YUV10_1X30: -+ case MEDIA_BUS_FMT_UYVY10_1X20: -+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: -+ return 10; -+ -+ case MEDIA_BUS_FMT_RGB121212_1X36: -+ case MEDIA_BUS_FMT_YUV12_1X36: -+ case MEDIA_BUS_FMT_UYVY12_1X24: -+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: -+ return 12; -+ -+ case MEDIA_BUS_FMT_RGB161616_1X48: -+ case MEDIA_BUS_FMT_YUV16_1X48: -+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: -+ return 16; -+ -+ default: -+ return 0; -+ } -+} -+ -+static void dw_hdmi_i2c_init(struct dw_hdmi_qp *hdmi) -+{ -+ /* Software reset */ -+ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); -+ -+ hdmi_writel(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0); -+ -+ hdmi_modb(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0); -+ -+ /* Clear DONE and ERROR interrupts */ -+ hdmi_writel(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR, -+ MAINUNIT_1_INT_CLEAR); -+} -+ -+static int dw_hdmi_i2c_read(struct dw_hdmi_qp *hdmi, -+ unsigned char *buf, unsigned int length) -+{ -+ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; -+ int stat; -+ -+ if (!i2c->is_regaddr) { -+ dev_dbg(hdmi->dev, "set read register address to 0\n"); -+ i2c->slave_reg = 0x00; -+ i2c->is_regaddr = true; -+ } -+ -+ while (length--) { -+ reinit_completion(&i2c->cmp); -+ -+ hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, -+ I2CM_INTERFACE_CONTROL0); -+ -+ hdmi_modb(hdmi, I2CM_FM_READ, I2CM_WR_MASK, -+ I2CM_INTERFACE_CONTROL0); -+ -+ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); -+ if (!stat) { -+ dev_err(hdmi->dev, "i2c read time out!\n"); -+ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); -+ return -EAGAIN; -+ } -+ -+ /* Check for error condition on the bus */ -+ if (i2c->stat & I2CM_NACK_RCVD_IRQ) { -+ dev_err(hdmi->dev, "i2c read err!\n"); -+ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); -+ return -EIO; -+ } -+ -+ *buf++ = hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff; -+ //dev_dbg(hdmi->dev, "i2c read done! i2c->stat:%02x 0x%02x\n", i2c->stat, hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3)); -+ hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); -+ } -+ i2c->is_segment = false; -+ -+ return 0; -+} -+ -+static int dw_hdmi_i2c_write(struct dw_hdmi_qp *hdmi, -+ unsigned char *buf, unsigned int length) -+{ -+ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; -+ int stat; -+ -+ if (!i2c->is_regaddr) { -+ /* Use the first write byte as register address */ -+ i2c->slave_reg = buf[0]; -+ length--; -+ buf++; -+ i2c->is_regaddr = true; -+ } -+ -+ while (length--) { -+ reinit_completion(&i2c->cmp); -+ -+ hdmi_writel(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3); -+ hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, -+ I2CM_INTERFACE_CONTROL0); -+ hdmi_modb(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK, -+ I2CM_INTERFACE_CONTROL0); -+ -+ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); -+ if (!stat) { -+ dev_err(hdmi->dev, "i2c write time out!\n"); -+ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); -+ return -EAGAIN; -+ } -+ -+ /* Check for error condition on the bus */ -+ if (i2c->stat & I2CM_NACK_RCVD_IRQ) { -+ dev_err(hdmi->dev, "i2c write nack!\n"); -+ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); -+ return -EIO; -+ } -+ hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); -+ } -+ dev_dbg(hdmi->dev, "i2c write done!\n"); -+ return 0; -+} -+ -+static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, -+ struct i2c_msg *msgs, int num) -+{ -+ struct dw_hdmi_qp *hdmi = i2c_get_adapdata(adap); -+ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; -+ u8 addr = msgs[0].addr; -+ int i, ret = 0; -+ -+ if (addr == DDC_CI_ADDR) -+ /* -+ * The internal I2C controller does not support the multi-byte -+ * read and write operations needed for DDC/CI. -+ * TOFIX: Blacklist the DDC/CI address until we filter out -+ * unsupported I2C operations. -+ */ -+ return -EOPNOTSUPP; -+ -+ dev_dbg(hdmi->dev, "i2c xfer: num: %d, addr: %#x\n", num, addr); -+ -+ for (i = 0; i < num; i++) { -+ if (msgs[i].len == 0) { -+ dev_err(hdmi->dev, -+ "unsupported transfer %d/%d, no data\n", -+ i + 1, num); -+ return -EOPNOTSUPP; -+ } -+ } -+ -+ mutex_lock(&i2c->lock); -+ -+ /* Unmute DONE and ERROR interrupts */ -+ hdmi_modb(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, -+ I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, -+ MAINUNIT_1_INT_MASK_N); -+ -+ /* Set slave device address taken from the first I2C message */ -+ if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1) -+ addr = DDC_ADDR; -+ -+ hdmi_modb(hdmi, addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0); -+ -+ /* Set slave device register address on transfer */ -+ i2c->is_regaddr = false; -+ -+ /* Set segment pointer for I2C extended read mode operation */ -+ i2c->is_segment = false; -+ -+ for (i = 0; i < num; i++) { -+ dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n", -+ i + 1, num, msgs[i].len, msgs[i].flags); -+ -+ if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { -+ i2c->is_segment = true; -+ hdmi_modb(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR, -+ I2CM_INTERFACE_CONTROL1); -+ hdmi_modb(hdmi, *msgs[i].buf, I2CM_SEG_PTR, -+ I2CM_INTERFACE_CONTROL1); -+ } else { -+ if (msgs[i].flags & I2C_M_RD) -+ ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, -+ msgs[i].len); -+ else -+ ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, -+ msgs[i].len); -+ } -+ if (ret < 0) -+ break; -+ } -+ -+ if (!ret) -+ ret = num; -+ -+ /* Mute DONE and ERROR interrupts */ -+ hdmi_modb(hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N, -+ MAINUNIT_1_INT_MASK_N); -+ -+ mutex_unlock(&i2c->lock); -+ -+ return ret; -+} -+ -+static u32 dw_hdmi_i2c_func(struct i2c_adapter *adapter) -+{ -+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -+} -+ -+static const struct i2c_algorithm dw_hdmi_algorithm = { -+ .master_xfer = dw_hdmi_i2c_xfer, -+ .functionality = dw_hdmi_i2c_func, -+}; -+ -+static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi_qp *hdmi) -+{ -+ struct i2c_adapter *adap; -+ struct dw_hdmi_qp_i2c *i2c; -+ int ret; -+ -+ i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL); -+ if (!i2c) -+ return ERR_PTR(-ENOMEM); -+ -+ mutex_init(&i2c->lock); -+ init_completion(&i2c->cmp); -+ -+ adap = &i2c->adap; -+ adap->owner = THIS_MODULE; -+ adap->dev.parent = hdmi->dev; -+ adap->algo = &dw_hdmi_algorithm; -+ strscpy(adap->name, "ddc", sizeof(adap->name)); -+ i2c_set_adapdata(adap, hdmi); -+ -+ ret = i2c_add_adapter(adap); -+ if (ret) { -+ dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name); -+ devm_kfree(hdmi->dev, i2c); -+ return ERR_PTR(ret); -+ } -+ -+ hdmi->i2c = i2c; -+ -+ dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name); -+ -+ return adap; -+} -+ -+#define HDMI_PHY_EARC_MASK BIT(29) -+ -+int dw_hdmi_qp_set_earc(struct dw_hdmi_qp *hdmi) -+{ -+ u32 stat, ret; -+ -+ /* set hdmi phy earc mode */ -+ hdmi->phy.ops->set_mode(hdmi, hdmi->phy.data, HDMI_PHY_EARC_MASK, -+ true); -+ -+ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, -+ &hdmi->previous_mode); -+ if (ret) -+ return ret; -+ -+ reinit_completion(&hdmi->earc_cmp); -+ -+ hdmi_modb(hdmi, EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ | -+ EARCRX_CMDC_DISCOVERY_DONE_IRQ, -+ EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ | -+ EARCRX_CMDC_DISCOVERY_DONE_IRQ, EARCRX_0_INT_MASK_N); -+ -+ /* start discovery */ -+ hdmi_modb(hdmi, EARCRX_CMDC_DISCOVERY_EN, EARCRX_CMDC_DISCOVERY_EN, -+ EARCRX_CMDC_CONTROL); -+ -+ /* -+ * The eARC TX device drives a logic-high-voltage-level -+ * pulse on the physical HPD connector pin, after -+ * at least 100 ms of low voltage level to start the -+ * eARC Discovery process. -+ */ -+ hdmi_modb(hdmi, EARCRX_CONNECTOR_HPD, EARCRX_CONNECTOR_HPD, -+ EARCRX_CMDC_CONTROL); -+ -+ stat = wait_for_completion_timeout(&hdmi->earc_cmp, HZ / 10); -+ if (!stat) -+ return -EAGAIN; -+ -+ if (hdmi->earc_intr & EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ) { -+ dev_err(hdmi->dev, "discovery timeout\n"); -+ return -ETIMEDOUT; -+ } else if (hdmi->earc_intr & EARCRX_CMDC_DISCOVERY_DONE_IRQ) { -+ dev_info(hdmi->dev, "discovery done\n"); -+ } else { -+ dev_err(hdmi->dev, "discovery failed\n"); -+ return -EINVAL; -+ } -+ -+ hdmi_writel(hdmi, 1, EARCRX_DMAC_PHY_CONTROL); -+ hdmi_modb(hdmi, EARCRX_CMDC_SWINIT_P, EARCRX_CMDC_SWINIT_P, -+ EARCRX_CMDC_CONFIG0); -+ -+ hdmi_writel(hdmi, 0xf3, EARCRX_DMAC_CONFIG); -+ hdmi_writel(hdmi, 0x63, EARCRX_DMAC_CONTROL0); -+ hdmi_writel(hdmi, 0xff, EARCRX_DMAC_CONTROL1); -+ -+ hdmi_modb(hdmi, EARCRX_XACTREAD_STOP_CFG | EARCRX_XACTREAD_RETRY_CFG | -+ EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 | EARCRX_CMDC_XACT_RESTART_EN, -+ EARCRX_XACTREAD_STOP_CFG | EARCRX_XACTREAD_RETRY_CFG | -+ EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 | EARCRX_CMDC_XACT_RESTART_EN, -+ EARCRX_CMDC_CONFIG0); -+ -+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER0); -+ hdmi_writel(hdmi, 0x1b0e, EARCRX_DMAC_CHSTATUS_STREAMER1); -+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER2); -+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER3); -+ hdmi_writel(hdmi, 0xf2000000, EARCRX_DMAC_CHSTATUS_STREAMER4); -+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER5); -+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER6); -+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER7); -+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER8); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_earc); -+ -+/* ----------------------------------------------------------------------------- -+ * HDMI TX Setup -+ */ -+ -+static void hdmi_infoframe_set_checksum(u8 *ptr, int size) -+{ -+ u8 csum = 0; -+ int i; -+ -+ ptr[3] = 0; -+ /* compute checksum */ -+ for (i = 0; i < size; i++) -+ csum += ptr[i]; -+ -+ ptr[3] = 256 - csum; -+} -+ -+static void hdmi_config_AVI(struct dw_hdmi_qp *hdmi, -+ const struct drm_connector *connector, -+ const struct drm_display_mode *mode) -+{ -+ struct hdmi_avi_infoframe frame; -+ u32 val, i, j; -+ u8 buff[17]; -+ enum hdmi_quantization_range rgb_quant_range = -+ hdmi->hdmi_data.quant_range; -+ -+ /* Initialise info frame from DRM mode */ -+ drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode); -+ -+ /* -+ * Ignore monitor selectable quantization, use quantization set -+ * by the user -+ */ -+ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, rgb_quant_range); -+ if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) -+ frame.colorspace = HDMI_COLORSPACE_YUV444; -+ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) -+ frame.colorspace = HDMI_COLORSPACE_YUV422; -+ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) -+ frame.colorspace = HDMI_COLORSPACE_YUV420; -+ else -+ frame.colorspace = HDMI_COLORSPACE_RGB; -+ -+ /* Set up colorimetry */ -+ if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { -+ switch (hdmi->hdmi_data.enc_out_encoding) { -+ case V4L2_YCBCR_ENC_601: -+ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601) -+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; -+ else -+ frame.colorimetry = HDMI_COLORIMETRY_ITU_601; -+ frame.extended_colorimetry = -+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; -+ break; -+ case V4L2_YCBCR_ENC_709: -+ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709) -+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; -+ else -+ frame.colorimetry = HDMI_COLORIMETRY_ITU_709; -+ frame.extended_colorimetry = -+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_709; -+ break; -+ case V4L2_YCBCR_ENC_BT2020: -+ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_BT2020) -+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; -+ else -+ frame.colorimetry = HDMI_COLORIMETRY_ITU_709; -+ frame.extended_colorimetry = -+ HDMI_EXTENDED_COLORIMETRY_BT2020; -+ break; -+ default: /* Carries no data */ -+ frame.colorimetry = HDMI_COLORIMETRY_ITU_601; -+ frame.extended_colorimetry = -+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; -+ break; -+ } -+ } else { -+ if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { -+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; -+ frame.extended_colorimetry = -+ HDMI_EXTENDED_COLORIMETRY_BT2020; -+ } else { -+ frame.colorimetry = HDMI_COLORIMETRY_NONE; -+ frame.extended_colorimetry = -+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; -+ } -+ } -+ -+ frame.scan_mode = HDMI_SCAN_MODE_NONE; -+ frame.video_code = hdmi->vic; -+ -+ hdmi_avi_infoframe_pack_only(&frame, buff, 17); -+ -+ /* mode which vic >= 128 must use avi version 3 */ -+ if (hdmi->vic >= 128) { -+ frame.version = 3; -+ buff[1] = frame.version; -+ buff[4] &= 0x1f; -+ buff[4] |= ((frame.colorspace & 0x7) << 5); -+ buff[7] = frame.video_code; -+ hdmi_infoframe_set_checksum(buff, 17); -+ } -+ -+ /* -+ * The Designware IP uses a different byte format from standard -+ * AVI info frames, though generally the bits are in the correct -+ * bytes. -+ */ -+ -+ val = (frame.version << 8) | (frame.length << 16); -+ hdmi_writel(hdmi, val, PKT_AVI_CONTENTS0); -+ -+ for (i = 0; i < 4; i++) { -+ for (j = 0; j < 4; j++) { -+ if (i * 4 + j >= 14) -+ break; -+ if (!j) -+ val = buff[i * 4 + j + 3]; -+ val |= buff[i * 4 + j + 3] << (8 * j); -+ } -+ -+ hdmi_writel(hdmi, val, PKT_AVI_CONTENTS1 + i * 4); -+ } -+ -+ hdmi_modb(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1); -+ -+ hdmi_modb(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, -+ PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, -+ PKTSCHED_PKT_EN); -+} -+ -+static void hdmi_config_CVTEM(struct dw_hdmi_qp *hdmi) -+{ -+ u8 ds_type = 0; -+ u8 sync = 1; -+ u8 vfr = 1; -+ u8 afr = 0; -+ u8 new = 1; -+ u8 end = 0; -+ u8 data_set_length = 136; -+ u8 hb1[6] = { 0x80, 0, 0, 0, 0, 0x40 }; -+ u8 *pps_body; -+ u32 val, i, reg; -+ struct drm_display_mode *mode = &hdmi->previous_mode; -+ int hsync, hfront, hback; -+ struct dw_hdmi_link_config *link_cfg; -+ void *data = hdmi->plat_data->phy_data; -+ -+ hdmi_modb(hdmi, 0, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_PKT_EN); -+ -+ if (hdmi->plat_data->get_link_cfg) { -+ link_cfg = hdmi->plat_data->get_link_cfg(data); -+ } else { -+ dev_err(hdmi->dev, "can't get frl link cfg\n"); -+ return; -+ } -+ -+ if (!link_cfg->dsc_mode) { -+ dev_info(hdmi->dev, "don't use dsc mode\n"); -+ return; -+ } -+ -+ pps_body = link_cfg->pps_payload; -+ -+ hsync = mode->hsync_end - mode->hsync_start; -+ hback = mode->htotal - mode->hsync_end; -+ hfront = mode->hsync_start - mode->hdisplay; -+ -+ for (i = 0; i < 6; i++) { -+ val = i << 16 | hb1[i] << 8; -+ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS0 + i * 0x20); -+ } -+ -+ val = new << 7 | end << 6 | ds_type << 4 | afr << 3 | -+ vfr << 2 | sync << 1; -+ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS1); -+ -+ val = data_set_length << 16 | pps_body[0] << 24; -+ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS2); -+ -+ reg = PKT0_EMP_CVTEM_CONTENTS3; -+ for (i = 1; i < 125; i++) { -+ if (reg == PKT1_EMP_CVTEM_CONTENTS0 || -+ reg == PKT2_EMP_CVTEM_CONTENTS0 || -+ reg == PKT3_EMP_CVTEM_CONTENTS0 || -+ reg == PKT4_EMP_CVTEM_CONTENTS0 || -+ reg == PKT5_EMP_CVTEM_CONTENTS0) { -+ reg += 4; -+ i--; -+ continue; -+ } -+ if (i % 4 == 1) -+ val = pps_body[i]; -+ if (i % 4 == 2) -+ val |= pps_body[i] << 8; -+ if (i % 4 == 3) -+ val |= pps_body[i] << 16; -+ if (!(i % 4)) { -+ val |= pps_body[i] << 24; -+ hdmi_writel(hdmi, val, reg); -+ reg += 4; -+ } -+ } -+ -+ val = (hfront & 0xff) << 24 | pps_body[127] << 16 | -+ pps_body[126] << 8 | pps_body[125]; -+ hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS6); -+ -+ val = (hback & 0xff) << 24 | ((hsync >> 8) & 0xff) << 16 | -+ (hsync & 0xff) << 8 | ((hfront >> 8) & 0xff); -+ hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS7); -+ -+ val = link_cfg->hcactive << 8 | ((hback >> 8) & 0xff); -+ hdmi_writel(hdmi, val, PKT5_EMP_CVTEM_CONTENTS1); -+ -+ for (i = PKT5_EMP_CVTEM_CONTENTS2; i <= PKT5_EMP_CVTEM_CONTENTS7; i += 4) -+ hdmi_writel(hdmi, 0, i); -+ -+ hdmi_modb(hdmi, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_EMP_CVTEM_TX_EN, -+ PKTSCHED_PKT_EN); -+} -+ -+static void hdmi_config_drm_infoframe(struct dw_hdmi_qp *hdmi, -+ const struct drm_connector *connector) -+{ -+ const struct drm_connector_state *conn_state = connector->state; -+ struct hdr_output_metadata *hdr_metadata; -+ struct hdmi_drm_infoframe frame; -+ u8 buffer[30]; -+ ssize_t err; -+ int i; -+ u32 val; -+ -+ if (!hdmi->plat_data->use_drm_infoframe) -+ return; -+ -+ hdmi_modb(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); -+ -+ if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) { -+ DRM_DEBUG("No need to set HDR metadata in infoframe\n"); -+ return; -+ } -+ -+ if (!conn_state->hdr_output_metadata) { -+ DRM_DEBUG("source metadata not set yet\n"); -+ return; -+ } -+ -+ hdr_metadata = (struct hdr_output_metadata *) -+ conn_state->hdr_output_metadata->data; -+ -+ if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf & -+ BIT(hdr_metadata->hdmi_metadata_type1.eotf))) { -+ DRM_ERROR("Not support EOTF %d\n", -+ hdr_metadata->hdmi_metadata_type1.eotf); -+ return; -+ } -+ -+ err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); -+ if (err < 0) -+ return; -+ -+ err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer)); -+ if (err < 0) { -+ dev_err(hdmi->dev, "Failed to pack drm infoframe: %zd\n", err); -+ return; -+ } -+ -+ val = (frame.version << 8) | (frame.length << 16); -+ hdmi_writel(hdmi, val, PKT_DRMI_CONTENTS0); -+ -+ for (i = 0; i <= frame.length; i++) { -+ if (i % 4 == 0) -+ val = buffer[3 + i]; -+ val |= buffer[3 + i] << ((i % 4) * 8); -+ -+ if (i % 4 == 3 || (i == (frame.length))) -+ hdmi_writel(hdmi, val, PKT_DRMI_CONTENTS1 + ((i / 4) * 4)); -+ } -+ -+ hdmi_modb(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1); -+ -+ hdmi_modb(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); -+ -+ DRM_DEBUG("%s eotf %d end\n", __func__, -+ hdr_metadata->hdmi_metadata_type1.eotf); -+} -+ -+/* Filter out invalid setups to avoid configuring SCDC and scrambling */ -+static bool dw_hdmi_support_scdc(struct dw_hdmi_qp *hdmi, -+ const struct drm_display_info *display) -+{ -+ /* Disable if no DDC bus */ -+ if (!hdmi->ddc) -+ return false; -+ -+ /* Disable if SCDC is not supported, or if an HF-VSDB block is absent */ -+ if (!display->hdmi.scdc.supported || -+ !display->hdmi.scdc.scrambling.supported) -+ return false; -+ -+ /* -+ * Disable if display only support low TMDS rates and scrambling -+ * for low rates is not supported either -+ */ -+ if (!display->hdmi.scdc.scrambling.low_rates && -+ display->max_tmds_clock <= 340000) -+ return false; -+ -+ return true; -+} -+ -+static int hdmi_set_frl_mask(int frl_rate) -+{ -+ switch (frl_rate) { -+ case 48: -+ return FRL_12GBPS_4LANE; -+ case 40: -+ return FRL_10GBPS_4LANE; -+ case 32: -+ return FRL_8GBPS_4LANE; -+ case 24: -+ return FRL_6GBPS_4LANE; -+ case 18: -+ return FRL_6GBPS_3LANE; -+ case 9: -+ return FRL_3GBPS_3LANE; -+ } -+ -+ return 0; -+} -+ -+static int hdmi_start_flt(struct dw_hdmi_qp *hdmi, u8 rate) -+{ -+ u8 val; -+ u8 ffe_lv = 0; -+ int i = 0, stat; -+ -+ /* FLT_READY & FFE_LEVELS read */ -+ for (i = 0; i < 20; i++) { -+ drm_scdc_readb(hdmi->ddc, SCDC_STATUS_FLAGS_0, &val); -+ if (val & BIT(6)) -+ break; -+ msleep(20); -+ } -+ -+ if (i == 20) { -+ dev_err(hdmi->dev, "sink flt isn't ready\n"); -+ return -EINVAL; -+ } -+ -+ hdmi_modb(hdmi, SCDC_UPD_FLAGS_RD_IRQ, SCDC_UPD_FLAGS_RD_IRQ, -+ MAINUNIT_1_INT_MASK_N); -+ hdmi_modb(hdmi, SCDC_UPD_FLAGS_POLL_EN | SCDC_UPD_FLAGS_AUTO_CLR, -+ SCDC_UPD_FLAGS_POLL_EN | SCDC_UPD_FLAGS_AUTO_CLR, -+ SCDC_CONFIG0); -+ -+ /* max ffe level 3 */ -+ val = 3 << 4 | hdmi_set_frl_mask(rate); -+ drm_scdc_writeb(hdmi->ddc, 0x31, val); -+ -+ /* select FRL_RATE & FFE_LEVELS */ -+ hdmi_writel(hdmi, ffe_lv, FLT_CONFIG0); -+ -+ /* Start LTS_3 state in source DUT */ -+ reinit_completion(&hdmi->flt_cmp); -+ hdmi_modb(hdmi, FLT_EXIT_TO_LTSP_IRQ, FLT_EXIT_TO_LTSP_IRQ, -+ MAINUNIT_1_INT_MASK_N); -+ hdmi_writel(hdmi, 1, FLT_CONTROL0); -+ -+ /* wait for completed link training at source side */ -+ stat = wait_for_completion_timeout(&hdmi->flt_cmp, HZ * 2); -+ if (!stat) { -+ dev_err(hdmi->dev, "wait lts3 finish time out\n"); -+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | -+ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); -+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, -+ MAINUNIT_1_INT_MASK_N); -+ return -EAGAIN; -+ } -+ -+ if (!(hdmi->flt_intr & FLT_EXIT_TO_LTSP_IRQ)) { -+ dev_err(hdmi->dev, "not to ltsp\n"); -+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | -+ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); -+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, -+ MAINUNIT_1_INT_MASK_N); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+#define HDMI_MODE_FRL_MASK BIT(30) -+ -+static void hdmi_set_op_mode(struct dw_hdmi_qp *hdmi, -+ struct dw_hdmi_link_config *link_cfg, -+ const struct drm_connector *connector) -+{ -+ int frl_rate; -+ int i; -+ -+ /* set sink frl mode disable and wait sink ready */ -+ hdmi_writel(hdmi, 0, FLT_CONFIG0); -+ if (dw_hdmi_support_scdc(hdmi, &connector->display_info)) -+ drm_scdc_writeb(hdmi->ddc, 0x31, 0); -+ /* -+ * some TVs must wait a while before switching frl mode resolution, -+ * or the signal may not be recognized. -+ */ -+ msleep(200); -+ -+ if (!link_cfg->frl_mode) { -+ dev_info(hdmi->dev, "dw hdmi qp use tmds mode\n"); -+ hdmi_modb(hdmi, 0, OPMODE_FRL, LINK_CONFIG0); -+ hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0); -+ return; -+ } -+ -+ if (link_cfg->frl_lanes == 4) -+ hdmi_modb(hdmi, OPMODE_FRL_4LANES, OPMODE_FRL_4LANES, -+ LINK_CONFIG0); -+ else -+ hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0); -+ -+ hdmi_modb(hdmi, 1, OPMODE_FRL, LINK_CONFIG0); -+ -+ frl_rate = link_cfg->frl_lanes * link_cfg->rate_per_lane; -+ hdmi_start_flt(hdmi, frl_rate); -+ -+ for (i = 0; i < 50; i++) { -+ hdmi_modb(hdmi, PKTSCHED_NULL_TX_EN, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN); -+ mdelay(1); -+ hdmi_modb(hdmi, 0, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN); -+ } -+} -+ -+static unsigned long -+hdmi_get_tmdsclock(struct dw_hdmi_qp *hdmi, unsigned long mpixelclock) -+{ -+ unsigned long tmdsclock = mpixelclock; -+ unsigned int depth = -+ hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); -+ -+ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { -+ switch (depth) { -+ case 16: -+ tmdsclock = mpixelclock * 2; -+ break; -+ case 12: -+ tmdsclock = mpixelclock * 3 / 2; -+ break; -+ case 10: -+ tmdsclock = mpixelclock * 5 / 4; -+ break; -+ default: -+ break; -+ } -+ } -+ -+ return tmdsclock; -+} -+ -+static int dw_hdmi_qp_setup(struct dw_hdmi_qp *hdmi, -+ const struct drm_connector *connector, -+ struct drm_display_mode *mode) -+{ -+ int ret; -+ void *data = hdmi->plat_data->phy_data; -+ struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode; -+ struct dw_hdmi_link_config *link_cfg; -+ u8 bytes = 0; -+ -+ hdmi->vic = drm_match_cea_mode(mode); -+ if (!hdmi->vic) -+ dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n"); -+ else -+ dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic); -+ -+ if (hdmi->plat_data->get_enc_out_encoding) -+ hdmi->hdmi_data.enc_out_encoding = -+ hdmi->plat_data->get_enc_out_encoding(data); -+ else if ((hdmi->vic == 6) || (hdmi->vic == 7) || -+ (hdmi->vic == 21) || (hdmi->vic == 22) || -+ (hdmi->vic == 2) || (hdmi->vic == 3) || -+ (hdmi->vic == 17) || (hdmi->vic == 18)) -+ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601; -+ else -+ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709; -+ -+ if (mode->flags & DRM_MODE_FLAG_DBLCLK) { -+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1; -+ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1; -+ } else { -+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; -+ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; -+ } -+ /* Get input format from plat data or fallback to RGB888 */ -+ if (hdmi->plat_data->get_input_bus_format) -+ hdmi->hdmi_data.enc_in_bus_format = -+ hdmi->plat_data->get_input_bus_format(data); -+ else if (hdmi->plat_data->input_bus_format) -+ hdmi->hdmi_data.enc_in_bus_format = -+ hdmi->plat_data->input_bus_format; -+ else -+ hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24; -+ -+ /* Default to RGB888 output format */ -+ if (hdmi->plat_data->get_output_bus_format) -+ hdmi->hdmi_data.enc_out_bus_format = -+ hdmi->plat_data->get_output_bus_format(data); -+ else -+ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24; -+ -+ /* Get input encoding from plat data or fallback to none */ -+ if (hdmi->plat_data->get_enc_in_encoding) -+ hdmi->hdmi_data.enc_in_encoding = -+ hdmi->plat_data->get_enc_in_encoding(data); -+ else if (hdmi->plat_data->input_bus_encoding) -+ hdmi->hdmi_data.enc_in_encoding = -+ hdmi->plat_data->input_bus_encoding; -+ else -+ hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT; -+ -+ if (hdmi->plat_data->get_quant_range) -+ hdmi->hdmi_data.quant_range = -+ hdmi->plat_data->get_quant_range(data); -+ else -+ hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT; -+ -+ if (hdmi->plat_data->get_link_cfg) -+ link_cfg = hdmi->plat_data->get_link_cfg(data); -+ else -+ return -EINVAL; -+ -+ hdmi->phy.ops->set_mode(hdmi, hdmi->phy.data, HDMI_MODE_FRL_MASK, -+ link_cfg->frl_mode); -+ -+ /* -+ * According to the dw-hdmi specification 6.4.2 -+ * vp_pr_cd[3:0]: -+ * 0000b: No pixel repetition (pixel sent only once) -+ * 0001b: Pixel sent two times (pixel repeated once) -+ */ -+ hdmi->hdmi_data.pix_repet_factor = -+ (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0; -+ hdmi->hdmi_data.video_mode.mdataenablepolarity = true; -+ -+ vmode->previous_pixelclock = vmode->mpixelclock; -+ if (hdmi->plat_data->split_mode) -+ mode->crtc_clock /= 2; -+ vmode->mpixelclock = mode->crtc_clock * 1000; -+ if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) -+ vmode->mpixelclock *= 2; -+ dev_dbg(hdmi->dev, "final pixclk = %ld\n", vmode->mpixelclock); -+ vmode->previous_tmdsclock = vmode->mtmdsclock; -+ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); -+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) -+ vmode->mtmdsclock /= 2; -+ dev_info(hdmi->dev, "final tmdsclk = %d\n", vmode->mtmdsclock); -+ -+ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, &hdmi->previous_mode); -+ if (ret) -+ return ret; -+ -+ if (hdmi->plat_data->set_grf_cfg) -+ hdmi->plat_data->set_grf_cfg(data); -+ -+ if (hdmi->sink_has_audio) { -+ dev_dbg(hdmi->dev, "sink has audio support\n"); -+ -+ /* HDMI Initialization Step E - Configure audio */ -+ hdmi_clk_regenerator_update_pixel_clock(hdmi); -+ hdmi_enable_audio_clk(hdmi, hdmi->audio_enable); -+ } -+ -+ /* not for DVI mode */ -+ if (hdmi->sink_is_hdmi) { -+ dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__); -+ hdmi_modb(hdmi, 0, OPMODE_DVI, LINK_CONFIG0); -+ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); -+ if (!link_cfg->frl_mode) { -+ if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK) { -+ drm_scdc_readb(hdmi->ddc, SCDC_SINK_VERSION, &bytes); -+ drm_scdc_writeb(hdmi->ddc, SCDC_SOURCE_VERSION, -+ min_t(u8, bytes, SCDC_MIN_SOURCE_VERSION)); -+ drm_scdc_set_high_tmds_clock_ratio((struct drm_connector *)connector, 1); -+ drm_scdc_set_scrambling((struct drm_connector *)connector, 1); -+ hdmi_writel(hdmi, 1, SCRAMB_CONFIG0); -+ } else { -+ if (dw_hdmi_support_scdc(hdmi, &connector->display_info)) { -+ drm_scdc_set_high_tmds_clock_ratio((struct drm_connector *)connector, 0); -+ drm_scdc_set_scrambling((struct drm_connector *)connector, 0); -+ } -+ hdmi_writel(hdmi, 0, SCRAMB_CONFIG0); -+ } -+ } -+ /* HDMI Initialization Step F - Configure AVI InfoFrame */ -+ hdmi_config_AVI(hdmi, connector, mode); -+ hdmi_config_CVTEM(hdmi); -+ hdmi_config_drm_infoframe(hdmi, connector); -+ hdmi_set_op_mode(hdmi, link_cfg, connector); -+ } else { -+ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); -+ hdmi_modb(hdmi, OPMODE_DVI, OPMODE_DVI, LINK_CONFIG0); -+ dev_info(hdmi->dev, "%s DVI mode\n", __func__); -+ } -+ -+ return 0; -+} -+ -+static enum drm_connector_status -+dw_hdmi_connector_detect(struct drm_connector *connector, bool force) -+{ -+ struct dw_hdmi_qp *hdmi = -+ container_of(connector, struct dw_hdmi_qp, connector); -+ struct dw_hdmi_qp *secondary = NULL; -+ enum drm_connector_status result, result_secondary; -+ -+ mutex_lock(&hdmi->mutex); -+ hdmi->force = DRM_FORCE_UNSPECIFIED; -+ mutex_unlock(&hdmi->mutex); -+ -+ if (hdmi->plat_data->left) -+ secondary = hdmi->plat_data->left; -+ else if (hdmi->plat_data->right) -+ secondary = hdmi->plat_data->right; -+ -+ result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); -+ -+ if (secondary) { -+ result_secondary = secondary->phy.ops->read_hpd(secondary, secondary->phy.data); -+ if (result == connector_status_connected && -+ result_secondary == connector_status_connected) -+ result = connector_status_connected; -+ else -+ result = connector_status_disconnected; -+ } -+ -+ mutex_lock(&hdmi->mutex); -+ if (result != hdmi->last_connector_result) { -+ dev_dbg(hdmi->dev, "read_hpd result: %d", result); -+ handle_plugged_change(hdmi, -+ result == connector_status_connected); -+ hdmi->last_connector_result = result; -+ } -+ mutex_unlock(&hdmi->mutex); -+ -+ return result; -+} -+ -+static int dw_hdmi_qp_connector_get_modes(struct drm_connector *connector) -+{ -+ struct dw_hdmi_qp *hdmi = -+ container_of(connector, struct dw_hdmi_qp, connector); -+ struct hdr_static_metadata *metedata = -+ &connector->hdr_sink_metadata.hdmi_type1; -+ struct edid *edid; -+ struct drm_display_mode *mode; -+ struct drm_display_info *info = &connector->display_info; -+ int i, ret = 0; -+ -+ if (!hdmi->ddc) -+ return 0; -+ -+ memset(metedata, 0, sizeof(*metedata)); -+ edid = drm_get_edid(connector, hdmi->ddc); -+ if (edid) { -+ dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", -+ edid->width_cm, edid->height_cm); -+ -+ hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); -+ hdmi->sink_has_audio = drm_detect_monitor_audio(edid); -+ drm_connector_update_edid_property(connector, edid); -+ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); -+ ret = drm_add_edid_modes(connector, edid); -+ kfree(edid); -+ } else { -+ hdmi->sink_is_hdmi = true; -+ hdmi->sink_has_audio = true; -+ -+ if (hdmi->plat_data->split_mode) { -+ if (hdmi->plat_data->left) { -+ hdmi->plat_data->left->sink_is_hdmi = true; -+ hdmi->plat_data->left->sink_has_audio = true; -+ } else if (hdmi->plat_data->right) { -+ hdmi->plat_data->right->sink_is_hdmi = true; -+ hdmi->plat_data->right->sink_has_audio = true; -+ } -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) { -+ const struct drm_display_mode *ptr = -+ &dw_hdmi_default_modes[i]; -+ -+ mode = drm_mode_duplicate(connector->dev, ptr); -+ if (mode) { -+ if (!i) -+ mode->type = DRM_MODE_TYPE_PREFERRED; -+ drm_mode_probed_add(connector, mode); -+ ret++; -+ } -+ } -+ if (ret > 0 && hdmi->plat_data->split_mode) { -+ struct drm_display_mode *mode; -+ -+ list_for_each_entry(mode, &connector->probed_modes, head) -+ hdmi->plat_data->convert_to_split_mode(mode); -+ } -+ info->edid_hdmi_rgb444_dc_modes = 0; -+ info->edid_hdmi_ycbcr444_dc_modes = 0; -+ info->hdmi.y420_dc_modes = 0; -+ info->color_formats = 0; -+ -+ dev_info(hdmi->dev, "failed to get edid\n"); -+ } -+ -+ return ret; -+} -+ -+static int -+dw_hdmi_atomic_connector_set_property(struct drm_connector *connector, -+ struct drm_connector_state *state, -+ struct drm_property *property, -+ uint64_t val) -+{ -+ struct dw_hdmi_qp *hdmi = -+ container_of(connector, struct dw_hdmi_qp, connector); -+ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops; -+ -+ if (ops && ops->set_property) -+ return ops->set_property(connector, state, property, -+ val, hdmi->plat_data->phy_data); -+ else -+ return -EINVAL; -+} -+ -+static int -+dw_hdmi_atomic_connector_get_property(struct drm_connector *connector, -+ const struct drm_connector_state *state, -+ struct drm_property *property, -+ uint64_t *val) -+{ -+ struct dw_hdmi_qp *hdmi = -+ container_of(connector, struct dw_hdmi_qp, connector); -+ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops; -+ -+ if (ops && ops->get_property) -+ return ops->get_property(connector, state, property, -+ val, hdmi->plat_data->phy_data); -+ else -+ return -EINVAL; -+} -+ -+static int -+dw_hdmi_connector_set_property(struct drm_connector *connector, -+ struct drm_property *property, uint64_t val) -+{ -+ return dw_hdmi_atomic_connector_set_property(connector, NULL, -+ property, val); -+} -+ -+static void dw_hdmi_attach_properties(struct dw_hdmi_qp *hdmi) -+{ -+ unsigned int color = MEDIA_BUS_FMT_RGB888_1X24; -+ const struct dw_hdmi_property_ops *ops = -+ hdmi->plat_data->property_ops; -+ -+ if (ops && ops->attach_properties) -+ return ops->attach_properties(&hdmi->connector, color, 0, -+ hdmi->plat_data->phy_data); -+} -+ -+static void dw_hdmi_destroy_properties(struct dw_hdmi_qp *hdmi) -+{ -+ const struct dw_hdmi_property_ops *ops = -+ hdmi->plat_data->property_ops; -+ -+ if (ops && ops->destroy_properties) -+ return ops->destroy_properties(&hdmi->connector, -+ hdmi->plat_data->phy_data); -+} -+ -+static struct drm_encoder * -+dw_hdmi_connector_best_encoder(struct drm_connector *connector) -+{ -+ struct dw_hdmi_qp *hdmi = -+ container_of(connector, struct dw_hdmi_qp, connector); -+ -+ return hdmi->bridge.encoder; -+} -+ -+static bool dw_hdmi_color_changed(struct drm_connector *connector, -+ struct drm_atomic_state *state) -+{ -+ struct dw_hdmi_qp *hdmi = -+ container_of(connector, struct dw_hdmi_qp, connector); -+ void *data = hdmi->plat_data->phy_data; -+ struct drm_connector_state *old_state = -+ drm_atomic_get_old_connector_state(state, connector); -+ struct drm_connector_state *new_state = -+ drm_atomic_get_new_connector_state(state, connector); -+ bool ret = false; -+ -+ if (hdmi->plat_data->get_color_changed) -+ ret = hdmi->plat_data->get_color_changed(data); -+ -+ if (new_state->colorspace != old_state->colorspace) -+ ret = true; -+ -+ return ret; -+} -+ -+static bool hdr_metadata_equal(const struct drm_connector_state *old_state, -+ const struct drm_connector_state *new_state) -+{ -+ struct drm_property_blob *old_blob = old_state->hdr_output_metadata; -+ struct drm_property_blob *new_blob = new_state->hdr_output_metadata; -+ -+ if (!old_blob || !new_blob) -+ return old_blob == new_blob; -+ -+ if (old_blob->length != new_blob->length) -+ return false; -+ -+ return !memcmp(old_blob->data, new_blob->data, old_blob->length); -+} -+ -+static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, -+ struct drm_atomic_state *state) -+{ -+ struct drm_connector_state *old_state = -+ drm_atomic_get_old_connector_state(state, connector); -+ struct drm_connector_state *new_state = -+ drm_atomic_get_new_connector_state(state, connector); -+ struct drm_crtc *crtc = new_state->crtc; -+ struct drm_crtc_state *crtc_state; -+ struct dw_hdmi_qp *hdmi = -+ container_of(connector, struct dw_hdmi_qp, connector); -+ struct drm_display_mode *mode = NULL; -+ void *data = hdmi->plat_data->phy_data; -+ struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode; -+ -+ if (!crtc) -+ return 0; -+ -+ crtc_state = drm_atomic_get_crtc_state(state, crtc); -+ if (IS_ERR(crtc_state)) -+ return PTR_ERR(crtc_state); -+ -+ /* -+ * If HDMI is enabled in uboot, it's need to record -+ * drm_display_mode and set phy status to enabled. -+ */ -+ if (!vmode->mpixelclock) { -+ crtc_state = drm_atomic_get_crtc_state(state, crtc); -+ if (hdmi->plat_data->get_enc_in_encoding) -+ hdmi->hdmi_data.enc_in_encoding = -+ hdmi->plat_data->get_enc_in_encoding(data); -+ if (hdmi->plat_data->get_enc_out_encoding) -+ hdmi->hdmi_data.enc_out_encoding = -+ hdmi->plat_data->get_enc_out_encoding(data); -+ if (hdmi->plat_data->get_input_bus_format) -+ hdmi->hdmi_data.enc_in_bus_format = -+ hdmi->plat_data->get_input_bus_format(data); -+ if (hdmi->plat_data->get_output_bus_format) -+ hdmi->hdmi_data.enc_out_bus_format = -+ hdmi->plat_data->get_output_bus_format(data); -+ -+ mode = &crtc_state->mode; -+ if (hdmi->plat_data->split_mode) { -+ hdmi->plat_data->convert_to_origin_mode(mode); -+ mode->crtc_clock /= 2; -+ } -+ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); -+ vmode->mpixelclock = mode->crtc_clock * 1000; -+ vmode->previous_pixelclock = mode->clock; -+ vmode->previous_tmdsclock = mode->clock; -+ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, -+ vmode->mpixelclock); -+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) -+ vmode->mtmdsclock /= 2; -+ -+ /* -+ * If uboot logo enabled, atomic_enable won't be called, -+ * but atomic_disable will be called when hdmi plug out. -+ * That will cause dclk enable count is incorrect. So -+ * we should check ipi/link/video clk to determine whether -+ * uboot logo is enabled. -+ */ -+ if (hdmi->initialized && !hdmi->dclk_en) { -+ mutex_lock(&hdmi->audio_mutex); -+ if (hdmi->plat_data->dclk_set) -+ hdmi->plat_data->dclk_set(data, true); -+ hdmi->dclk_en = true; -+ mutex_unlock(&hdmi->audio_mutex); -+ } -+ } -+ -+ if (!hdr_metadata_equal(old_state, new_state) || -+ dw_hdmi_color_changed(connector, state)) { -+ crtc_state = drm_atomic_get_crtc_state(state, crtc); -+ if (IS_ERR(crtc_state)) -+ return PTR_ERR(crtc_state); -+ -+ crtc_state->mode_changed = true; -+ } -+ -+ return 0; -+} -+ -+static void dw_hdmi_connector_force(struct drm_connector *connector) -+{ -+ struct dw_hdmi_qp *hdmi = -+ container_of(connector, struct dw_hdmi_qp, connector); -+ -+ mutex_lock(&hdmi->mutex); -+ -+ if (hdmi->force != connector->force) { -+ if (!hdmi->disabled && connector->force == DRM_FORCE_OFF) -+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, -+ false); -+ else if (hdmi->disabled && connector->force == DRM_FORCE_ON) -+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, -+ true); -+ } -+ -+ hdmi->force = connector->force; -+ mutex_unlock(&hdmi->mutex); -+} -+ -+static int dw_hdmi_qp_fill_modes(struct drm_connector *connector, u32 max_x, -+ u32 max_y) -+{ -+ return drm_helper_probe_single_connector_modes(connector, 9000, 9000); -+} -+ -+static const struct drm_connector_funcs dw_hdmi_connector_funcs = { -+ .fill_modes = dw_hdmi_qp_fill_modes, -+ .detect = dw_hdmi_connector_detect, -+ .destroy = drm_connector_cleanup, -+ .force = dw_hdmi_connector_force, -+ .reset = drm_atomic_helper_connector_reset, -+ .set_property = dw_hdmi_connector_set_property, -+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, -+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -+ .atomic_set_property = dw_hdmi_atomic_connector_set_property, -+ .atomic_get_property = dw_hdmi_atomic_connector_get_property, -+}; -+ -+static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { -+ .get_modes = dw_hdmi_qp_connector_get_modes, -+ .best_encoder = dw_hdmi_connector_best_encoder, -+ .atomic_check = dw_hdmi_connector_atomic_check, -+}; -+ -+static int dw_hdmi_qp_bridge_attach(struct drm_bridge *bridge, -+ enum drm_bridge_attach_flags flags) -+{ -+ struct dw_hdmi_qp *hdmi = bridge->driver_private; -+ struct drm_encoder *encoder = bridge->encoder; -+ struct drm_connector *connector = &hdmi->connector; -+ struct cec_connector_info conn_info; -+ struct cec_notifier *notifier; -+ -+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) -+ return 0; -+ -+ connector->interlace_allowed = 1; -+ connector->polled = DRM_CONNECTOR_POLL_HPD; -+ -+ drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs); -+ -+ drm_connector_init_with_ddc(bridge->dev, connector, &dw_hdmi_connector_funcs, -+ DRM_MODE_CONNECTOR_HDMIA, hdmi->ddc); -+ -+ drm_connector_attach_encoder(connector, encoder); -+ dw_hdmi_attach_properties(hdmi); -+ -+ cec_fill_conn_info_from_drm(&conn_info, connector); -+ notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info); -+ if (!notifier) -+ return -ENOMEM; -+ -+ mutex_lock(&hdmi->cec_notifier_mutex); -+ hdmi->cec_notifier = notifier; -+ mutex_unlock(&hdmi->cec_notifier_mutex); -+ -+ return 0; -+} -+ -+static void dw_hdmi_qp_bridge_detach(struct drm_bridge *bridge) -+{ -+ struct dw_hdmi_qp *hdmi = bridge->driver_private; -+ -+ mutex_lock(&hdmi->cec_notifier_mutex); -+ cec_notifier_conn_unregister(hdmi->cec_notifier); -+ hdmi->cec_notifier = NULL; -+ mutex_unlock(&hdmi->cec_notifier_mutex); -+} -+ -+static enum drm_mode_status -+dw_hdmi_qp_bridge_mode_valid(struct drm_bridge *bridge, -+ const struct drm_display_info *info, -+ const struct drm_display_mode *mode) -+{ -+ return MODE_OK; -+} -+ -+static void dw_hdmi_qp_bridge_mode_set(struct drm_bridge *bridge, -+ const struct drm_display_mode *orig_mode, -+ const struct drm_display_mode *mode) -+{ -+ struct dw_hdmi_qp *hdmi = bridge->driver_private; -+ -+ mutex_lock(&hdmi->mutex); -+ -+ /* Store the display mode for plugin/DKMS poweron events */ -+ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); -+ if (hdmi->plat_data->split_mode) -+ hdmi->plat_data->convert_to_origin_mode(&hdmi->previous_mode); -+ -+ mutex_unlock(&hdmi->mutex); -+} -+ -+static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, -+ struct drm_bridge_state *old_state) -+{ -+ struct dw_hdmi_qp *hdmi = bridge->driver_private; -+ void *data = hdmi->plat_data->phy_data; -+ -+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false); -+ handle_plugged_change(hdmi, false); -+ mutex_lock(&hdmi->mutex); -+ -+ hdmi->curr_conn = NULL; -+ -+ if (hdmi->dclk_en) { -+ mutex_lock(&hdmi->audio_mutex); -+ if (hdmi->plat_data->dclk_set) -+ hdmi->plat_data->dclk_set(data, false); -+ hdmi->dclk_en = false; -+ mutex_unlock(&hdmi->audio_mutex); -+ }; -+ -+ if (hdmi->phy.ops->disable) -+ hdmi->phy.ops->disable(hdmi, hdmi->phy.data); -+ hdmi->disabled = true; -+ mutex_unlock(&hdmi->mutex); -+} -+ -+static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, -+ struct drm_bridge_state *old_state) -+{ -+ struct dw_hdmi_qp *hdmi = bridge->driver_private; -+ struct drm_atomic_state *state = old_state->base.state; -+ struct drm_connector *connector; -+ void *data = hdmi->plat_data->phy_data; -+ -+ connector = drm_atomic_get_new_connector_for_encoder(state, -+ bridge->encoder); -+ -+ mutex_lock(&hdmi->mutex); -+ hdmi->curr_conn = connector; -+ dw_hdmi_qp_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); -+ hdmi->disabled = false; -+ mutex_unlock(&hdmi->mutex); -+ -+ if (!hdmi->dclk_en) { -+ mutex_lock(&hdmi->audio_mutex); -+ if (hdmi->plat_data->dclk_set) -+ hdmi->plat_data->dclk_set(data, true); -+ hdmi->dclk_en = true; -+ mutex_unlock(&hdmi->audio_mutex); -+ } -+ -+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true); -+ handle_plugged_change(hdmi, true); -+} -+ -+static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { -+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, -+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, -+ .atomic_reset = drm_atomic_helper_bridge_reset, -+ .attach = dw_hdmi_qp_bridge_attach, -+ .detach = dw_hdmi_qp_bridge_detach, -+ .mode_set = dw_hdmi_qp_bridge_mode_set, -+ .mode_valid = dw_hdmi_qp_bridge_mode_valid, -+ .atomic_enable = dw_hdmi_qp_bridge_atomic_enable, -+ .atomic_disable = dw_hdmi_qp_bridge_atomic_disable, -+}; -+ -+void dw_hdmi_qp_set_cec_adap(struct dw_hdmi_qp *hdmi, struct cec_adapter *adap) -+{ -+ hdmi->cec_adap = adap; -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_cec_adap); -+ -+static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id) -+{ -+ struct dw_hdmi_qp *hdmi = dev_id; -+ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; -+ u32 stat; -+ -+ stat = hdmi_readl(hdmi, MAINUNIT_1_INT_STATUS); -+ -+ i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ | -+ I2CM_NACK_RCVD_IRQ); -+ hdmi->scdc_intr = stat & (SCDC_UPD_FLAGS_RD_IRQ | -+ SCDC_UPD_FLAGS_CHG_IRQ | -+ SCDC_UPD_FLAGS_CLR_IRQ | -+ SCDC_RR_REPLY_STOP_IRQ | -+ SCDC_NACK_RCVD_IRQ); -+ hdmi->flt_intr = stat & (FLT_EXIT_TO_LTSP_IRQ | -+ FLT_EXIT_TO_LTS4_IRQ | -+ FLT_EXIT_TO_LTSL_IRQ); -+ -+ //dev_dbg(hdmi->dev, "i2c main unit irq:%#x\n", stat); -+ if (i2c->stat) { -+ hdmi_writel(hdmi, i2c->stat, MAINUNIT_1_INT_CLEAR); -+ complete(&i2c->cmp); -+ } -+ -+ if (hdmi->flt_intr) { -+ //dev_dbg(hdmi->dev, "i2c flt irq:%#x\n", hdmi->flt_intr); -+ hdmi_writel(hdmi, hdmi->flt_intr, MAINUNIT_1_INT_CLEAR); -+ complete(&hdmi->flt_cmp); -+ } -+ -+ if (hdmi->scdc_intr) { -+ u8 val; -+ -+ //dev_dbg(hdmi->dev, "i2c scdc irq:%#x\n", hdmi->scdc_intr); -+ hdmi_writel(hdmi, hdmi->scdc_intr, MAINUNIT_1_INT_CLEAR); -+ val = hdmi_readl(hdmi, SCDC_STATUS0); -+ -+ /* frl start */ -+ if (val & BIT(4)) { -+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | -+ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); -+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, -+ MAINUNIT_1_INT_MASK_N); -+ dev_info(hdmi->dev, "frl start\n"); -+ } -+ -+ } -+ -+ if (stat) -+ return IRQ_HANDLED; -+ -+ return IRQ_NONE; -+} -+ -+static irqreturn_t dw_hdmi_qp_avp_hardirq(int irq, void *dev_id) -+{ -+ struct dw_hdmi_qp *hdmi = dev_id; -+ u32 stat; -+ -+ stat = hdmi_readl(hdmi, AVP_1_INT_STATUS); -+ if (stat) { -+ dev_dbg(hdmi->dev, "HDCP irq %#x\n", stat); -+ stat &= ~stat; -+ hdmi_writel(hdmi, stat, AVP_1_INT_MASK_N); -+ return IRQ_WAKE_THREAD; -+ } -+ -+ return IRQ_NONE; -+} -+ -+static irqreturn_t dw_hdmi_qp_earc_hardirq(int irq, void *dev_id) -+{ -+ struct dw_hdmi_qp *hdmi = dev_id; -+ u32 stat; -+ -+ stat = hdmi_readl(hdmi, EARCRX_0_INT_STATUS); -+ if (stat) { -+ dev_dbg(hdmi->dev, "earc irq %#x\n", stat); -+ stat &= ~stat; -+ hdmi_writel(hdmi, stat, EARCRX_0_INT_MASK_N); -+ return IRQ_WAKE_THREAD; -+ } -+ -+ return IRQ_NONE; -+} -+ -+static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id) -+{ -+ struct dw_hdmi_qp *hdmi = dev_id; -+ u32 stat; -+ -+ stat = hdmi_readl(hdmi, AVP_1_INT_STATUS); -+ -+ if (!stat) -+ return IRQ_NONE; -+ -+ hdmi_writel(hdmi, stat, AVP_1_INT_CLEAR); -+ -+ return IRQ_HANDLED; -+} -+ -+static irqreturn_t dw_hdmi_qp_earc_irq(int irq, void *dev_id) -+{ -+ struct dw_hdmi_qp *hdmi = dev_id; -+ u32 stat; -+ -+ stat = hdmi_readl(hdmi, EARCRX_0_INT_STATUS); -+ -+ if (!stat) -+ return IRQ_NONE; -+ -+ hdmi_writel(hdmi, stat, EARCRX_0_INT_CLEAR); -+ -+ hdmi->earc_intr = stat; -+ complete(&hdmi->earc_cmp); -+ -+ return IRQ_HANDLED; -+} -+ -+static int dw_hdmi_detect_phy(struct dw_hdmi_qp *hdmi) -+{ -+ u8 phy_type; -+ -+ phy_type = hdmi->plat_data->phy_force_vendor ? -+ DW_HDMI_PHY_VENDOR_PHY : 0; -+ -+ if (phy_type == DW_HDMI_PHY_VENDOR_PHY) { -+ /* Vendor PHYs require support from the glue layer. */ -+ if (!hdmi->plat_data->qp_phy_ops || !hdmi->plat_data->phy_name) { -+ dev_err(hdmi->dev, -+ "Vendor HDMI PHY not supported by glue layer\n"); -+ return -ENODEV; -+ } -+ -+ hdmi->phy.ops = hdmi->plat_data->qp_phy_ops; -+ hdmi->phy.data = hdmi->plat_data->phy_data; -+ hdmi->phy.name = hdmi->plat_data->phy_name; -+ } -+ -+ return 0; -+} -+ -+void dw_hdmi_qp_cec_set_hpd(struct dw_hdmi_qp *hdmi, bool plug_in, bool change) -+{ -+ enum drm_connector_status status = plug_in ? -+ connector_status_connected : connector_status_disconnected; -+ -+ if (!plug_in) -+ cec_notifier_set_phys_addr(hdmi->cec_notifier, -+ CEC_PHYS_ADDR_INVALID); -+ -+ if (hdmi->bridge.dev) { -+ if (change && hdmi->cec_adap && hdmi->cec_adap->devnode.registered) -+ cec_queue_pin_hpd_event(hdmi->cec_adap, plug_in, ktime_get()); -+ drm_bridge_hpd_notify(&hdmi->bridge, status); -+ } -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_cec_set_hpd); -+ -+static void dw_hdmi_qp_cec_enable(struct dw_hdmi_qp *hdmi) -+{ -+ mutex_lock(&hdmi->mutex); -+ hdmi_modb(hdmi, 0, CEC_SWDISABLE, GLOBAL_SWDISABLE); -+ mutex_unlock(&hdmi->mutex); -+} -+ -+static void dw_hdmi_qp_cec_disable(struct dw_hdmi_qp *hdmi) -+{ -+ mutex_lock(&hdmi->mutex); -+ hdmi_modb(hdmi, CEC_SWDISABLE, CEC_SWDISABLE, GLOBAL_SWDISABLE); -+ mutex_unlock(&hdmi->mutex); -+} -+ -+static const struct dw_hdmi_qp_cec_ops dw_hdmi_qp_cec_ops = { -+ .enable = dw_hdmi_qp_cec_enable, -+ .disable = dw_hdmi_qp_cec_disable, -+ .write = hdmi_writel, -+ .read = hdmi_readl, -+}; -+ -+static const struct regmap_config hdmi_regmap_config = { -+ .reg_bits = 32, -+ .val_bits = 32, -+ .reg_stride = 4, -+ .max_register = EARCRX_1_INT_FORCE, -+}; -+ -+struct dw_hdmi_qp_reg_table { -+ int reg_base; -+ int reg_end; -+}; -+ -+static const struct dw_hdmi_qp_reg_table hdmi_reg_table[] = { -+ {0x0, 0xc}, -+ {0x14, 0x1c}, -+ {0x44, 0x48}, -+ {0x50, 0x58}, -+ {0x80, 0x84}, -+ {0xa0, 0xc4}, -+ {0xe0, 0xe8}, -+ {0xf0, 0x118}, -+ {0x140, 0x140}, -+ {0x150, 0x150}, -+ {0x160, 0x168}, -+ {0x180, 0x180}, -+ {0x800, 0x800}, -+ {0x808, 0x808}, -+ {0x814, 0x814}, -+ {0x81c, 0x824}, -+ {0x834, 0x834}, -+ {0x840, 0x864}, -+ {0x86c, 0x86c}, -+ {0x880, 0x89c}, -+ {0x8e0, 0x8e8}, -+ {0x900, 0x900}, -+ {0x908, 0x90c}, -+ {0x920, 0x938}, -+ {0x920, 0x938}, -+ {0x960, 0x960}, -+ {0x968, 0x968}, -+ {0xa20, 0xa20}, -+ {0xa30, 0xa30}, -+ {0xa40, 0xa40}, -+ {0xa54, 0xa54}, -+ {0xa80, 0xaac}, -+ {0xab4, 0xab8}, -+ {0xb00, 0xcbc}, -+ {0xce0, 0xce0}, -+ {0xd00, 0xddc}, -+ {0xe20, 0xe24}, -+ {0xe40, 0xe44}, -+ {0xe4c, 0xe4c}, -+ {0xe60, 0xe80}, -+ {0xea0, 0xf24}, -+ {0x1004, 0x100c}, -+ {0x1020, 0x1030}, -+ {0x1040, 0x1050}, -+ {0x1060, 0x1068}, -+ {0x1800, 0x1820}, -+ {0x182c, 0x182c}, -+ {0x1840, 0x1940}, -+ {0x1960, 0x1a60}, -+ {0x1b00, 0x1b00}, -+ {0x1c00, 0x1c00}, -+ {0x3000, 0x3000}, -+ {0x3010, 0x3014}, -+ {0x3020, 0x3024}, -+ {0x3800, 0x3800}, -+ {0x3810, 0x3814}, -+ {0x3820, 0x3824}, -+ {0x3830, 0x3834}, -+ {0x3840, 0x3844}, -+ {0x3850, 0x3854}, -+ {0x3860, 0x3864}, -+ {0x3870, 0x3874}, -+ {0x4000, 0x4004}, -+ {0x4800, 0x4800}, -+ {0x4810, 0x4814}, -+}; -+ -+static int dw_hdmi_ctrl_show(struct seq_file *s, void *v) -+{ -+ struct dw_hdmi_qp *hdmi = s->private; -+ u32 i = 0, j = 0, val = 0; -+ -+ seq_puts(s, "\n---------------------------------------------------"); -+ -+ for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) { -+ for (j = hdmi_reg_table[i].reg_base; -+ j <= hdmi_reg_table[i].reg_end; j += 4) { -+ val = hdmi_readl(hdmi, j); -+ -+ if ((j - hdmi_reg_table[i].reg_base) % 16 == 0) -+ seq_printf(s, "\n>>>hdmi_ctl %04x:", j); -+ seq_printf(s, " %08x", val); -+ } -+ } -+ seq_puts(s, "\n---------------------------------------------------\n"); -+ -+ return 0; -+} -+ -+static int dw_hdmi_ctrl_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, dw_hdmi_ctrl_show, inode->i_private); -+} -+ -+static ssize_t -+dw_hdmi_ctrl_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct dw_hdmi_qp *hdmi = -+ ((struct seq_file *)file->private_data)->private; -+ u32 reg, val; -+ char kbuf[25]; -+ -+ if (count > 24) { -+ dev_err(hdmi->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 > EARCRX_1_INT_FORCE) { -+ dev_err(hdmi->dev, "it is no a hdmi register\n"); -+ return count; -+ } -+ dev_info(hdmi->dev, "/**********hdmi register config******/"); -+ dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val); -+ hdmi_writel(hdmi, val, reg); -+ return count; -+} -+ -+static const struct file_operations dw_hdmi_ctrl_fops = { -+ .owner = THIS_MODULE, -+ .open = dw_hdmi_ctrl_open, -+ .read = seq_read, -+ .write = dw_hdmi_ctrl_write, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static int dw_hdmi_qp_status_show(struct seq_file *s, void *v) -+{ -+ struct dw_hdmi_qp *hdmi = s->private; -+ u32 val; -+ -+ seq_puts(s, "PHY: "); -+ if (hdmi->disabled) { -+ seq_puts(s, "disabled\n"); -+ return 0; -+ } -+ seq_puts(s, "enabled\t\t\tMode: "); -+ if (hdmi->sink_is_hdmi) -+ seq_puts(s, "HDMI\n"); -+ else -+ seq_puts(s, "DVI\n"); -+ -+ if (hdmi->hdmi_data.video_mode.mpixelclock > 600000000) { -+ seq_printf(s, "FRL Mode Pixel Clk: %luHz\n", -+ hdmi->hdmi_data.video_mode.mpixelclock); -+ } else { -+ if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000) -+ val = hdmi->hdmi_data.video_mode.mtmdsclock / 4; -+ else -+ val = hdmi->hdmi_data.video_mode.mtmdsclock; -+ seq_printf(s, "TMDS Mode Pixel Clk: %luHz\t\tTMDS Clk: %uHz\n", -+ hdmi->hdmi_data.video_mode.mpixelclock, val); -+ } -+ -+ seq_puts(s, "Color Format: "); -+ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) -+ seq_puts(s, "RGB"); -+ else if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) -+ seq_puts(s, "YUV444"); -+ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) -+ seq_puts(s, "YUV422"); -+ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) -+ seq_puts(s, "YUV420"); -+ else -+ seq_puts(s, "UNKNOWN"); -+ val = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); -+ seq_printf(s, "\t\tColor Depth: %d bit\n", val); -+ seq_puts(s, "Colorimetry: "); -+ switch (hdmi->hdmi_data.enc_out_encoding) { -+ case V4L2_YCBCR_ENC_601: -+ seq_puts(s, "ITU.BT601"); -+ break; -+ case V4L2_YCBCR_ENC_709: -+ seq_puts(s, "ITU.BT709"); -+ break; -+ case V4L2_YCBCR_ENC_BT2020: -+ seq_puts(s, "ITU.BT2020"); -+ break; -+ default: /* Carries no data */ -+ seq_puts(s, "ITU.BT601"); -+ break; -+ } -+ -+ seq_puts(s, "\t\tEOTF: "); -+ -+ val = hdmi_readl(hdmi, PKTSCHED_PKT_EN); -+ if (!(val & PKTSCHED_DRMI_TX_EN)) { -+ seq_puts(s, "Off\n"); -+ return 0; -+ } -+ -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS1); -+ val = (val >> 8) & 0x7; -+ switch (val) { -+ case HDMI_EOTF_TRADITIONAL_GAMMA_SDR: -+ seq_puts(s, "SDR"); -+ break; -+ case HDMI_EOTF_TRADITIONAL_GAMMA_HDR: -+ seq_puts(s, "HDR"); -+ break; -+ case HDMI_EOTF_SMPTE_ST2084: -+ seq_puts(s, "ST2084"); -+ break; -+ case HDMI_EOTF_BT_2100_HLG: -+ seq_puts(s, "HLG"); -+ break; -+ default: -+ seq_puts(s, "Not Defined\n"); -+ return 0; -+ } -+ -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS1); -+ val = (val >> 16) & 0xffff; -+ seq_printf(s, "\nx0: %d", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS2); -+ val = val & 0xffff; -+ seq_printf(s, "\t\t\t\ty0: %d\n", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS2); -+ val = (val >> 16) & 0xffff; -+ seq_printf(s, "x1: %d", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS3); -+ val = val & 0xffff; -+ seq_printf(s, "\t\t\t\ty1: %d\n", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS3); -+ val = (val >> 16) & 0xffff; -+ seq_printf(s, "x2: %d", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS4); -+ val = val & 0xffff; -+ seq_printf(s, "\t\t\t\ty2: %d\n", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS4); -+ val = (val >> 16) & 0xffff; -+ seq_printf(s, "white x: %d", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS5); -+ val = val & 0xffff; -+ seq_printf(s, "\t\t\twhite y: %d\n", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS5); -+ val = (val >> 16) & 0xffff; -+ seq_printf(s, "max lum: %d", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS6); -+ val = val & 0xffff; -+ seq_printf(s, "\t\t\tmin lum: %d\n", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS6); -+ val = (val >> 16) & 0xffff; -+ seq_printf(s, "max cll: %d", val); -+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS7); -+ val = val & 0xffff; -+ seq_printf(s, "\t\t\tmax fall: %d\n", val); -+ return 0; -+} -+ -+static int dw_hdmi_status_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, dw_hdmi_qp_status_show, inode->i_private); -+} -+ -+static const struct file_operations dw_hdmi_status_fops = { -+ .owner = THIS_MODULE, -+ .open = dw_hdmi_status_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi_qp *hdmi) -+{ -+ u8 buf[11]; -+ -+ snprintf(buf, sizeof(buf), "dw-hdmi%d", hdmi->plat_data->id); -+ hdmi->debugfs_dir = debugfs_create_dir(buf, NULL); -+ if (IS_ERR(hdmi->debugfs_dir)) { -+ dev_err(dev, "failed to create debugfs dir!\n"); -+ return; -+ } -+ -+ debugfs_create_file("status", 0400, hdmi->debugfs_dir, -+ hdmi, &dw_hdmi_status_fops); -+ debugfs_create_file("ctrl", 0600, hdmi->debugfs_dir, -+ hdmi, &dw_hdmi_ctrl_fops); -+} -+ -+static struct dw_hdmi_qp * -+__dw_hdmi_probe(struct platform_device *pdev, -+ const struct dw_hdmi_plat_data *plat_data) -+{ -+ struct device *dev = &pdev->dev; -+ struct device_node *np = dev->of_node; -+ struct device_node *ddc_node; -+ struct dw_hdmi_qp *hdmi; -+ struct dw_hdmi_qp_i2s_audio_data audio; -+ struct platform_device_info pdevinfo; -+ struct dw_hdmi_qp_cec_data cec; -+ struct resource *iores = NULL; -+ int irq; -+ int ret; -+ -+ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); -+ if (!hdmi) -+ return ERR_PTR(-ENOMEM); -+ -+ hdmi->connector.stereo_allowed = 1; -+ hdmi->plat_data = plat_data; -+ hdmi->dev = dev; -+ hdmi->sample_rate = 48000; -+ hdmi->disabled = true; -+ -+ mutex_init(&hdmi->mutex); -+ mutex_init(&hdmi->audio_mutex); -+ mutex_init(&hdmi->cec_notifier_mutex); -+ -+ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); -+ if (ddc_node) { -+ hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node); -+ of_node_put(ddc_node); -+ if (!hdmi->ddc) { -+ dev_dbg(hdmi->dev, "failed to read ddc node\n"); -+ return ERR_PTR(-EPROBE_DEFER); -+ } -+ -+ } else { -+ dev_dbg(hdmi->dev, "no ddc property found\n"); -+ } -+ -+ if (!plat_data->regm) { -+ const struct regmap_config *reg_config; -+ -+ reg_config = &hdmi_regmap_config; -+ -+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ hdmi->regs = devm_ioremap_resource(dev, iores); -+ if (IS_ERR(hdmi->regs)) { -+ ret = PTR_ERR(hdmi->regs); -+ goto err_res; -+ } -+ -+ hdmi->regm = devm_regmap_init_mmio(dev, hdmi->regs, reg_config); -+ if (IS_ERR(hdmi->regm)) { -+ dev_err(dev, "Failed to configure regmap\n"); -+ ret = PTR_ERR(hdmi->regm); -+ goto err_res; -+ } -+ } else { -+ hdmi->regm = plat_data->regm; -+ } -+ -+ ret = dw_hdmi_detect_phy(hdmi); -+ if (ret < 0) -+ goto err_res; -+ -+ hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N); -+ hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N); -+ hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0); -+ if ((hdmi_readl(hdmi, CMU_STATUS) & DISPLAY_CLK_MONITOR) == DISPLAY_CLK_LOCKED) { -+ hdmi->initialized = true; -+ hdmi->disabled = false; -+ } -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ ret = irq; -+ goto err_res; -+ } -+ -+ hdmi->avp_irq = irq; -+ ret = devm_request_threaded_irq(dev, hdmi->avp_irq, -+ dw_hdmi_qp_avp_hardirq, -+ dw_hdmi_qp_avp_irq, IRQF_SHARED, -+ dev_name(dev), hdmi); -+ if (ret) -+ goto err_res; -+ -+ irq = platform_get_irq(pdev, 1); -+ if (irq < 0) { -+ ret = irq; -+ goto err_res; -+ } -+ -+ cec.irq = irq; -+ -+ irq = platform_get_irq(pdev, 2); -+ if (irq < 0) { -+ ret = irq; -+ goto err_res; -+ } -+ -+ hdmi->earc_irq = irq; -+ ret = devm_request_threaded_irq(dev, hdmi->earc_irq, -+ dw_hdmi_qp_earc_hardirq, -+ dw_hdmi_qp_earc_irq, IRQF_SHARED, -+ dev_name(dev), hdmi); -+ if (ret) -+ goto err_res; -+ -+ irq = platform_get_irq(pdev, 3); -+ if (irq < 0) { -+ ret = irq; -+ goto err_res; -+ } -+ -+ hdmi->main_irq = irq; -+ ret = devm_request_threaded_irq(dev, hdmi->main_irq, -+ dw_hdmi_qp_main_hardirq, NULL, -+ IRQF_SHARED, dev_name(dev), hdmi); -+ if (ret) -+ goto err_res; -+ -+ hdmi_init_clk_regenerator(hdmi); -+ -+ /* If DDC bus is not specified, try to register HDMI I2C bus */ -+ if (!hdmi->ddc) { -+ hdmi->ddc = dw_hdmi_i2c_adapter(hdmi); -+ if (IS_ERR(hdmi->ddc)) -+ hdmi->ddc = NULL; -+ /* -+ * Read high and low time from device tree. If not available use -+ * the default timing scl clock rate is about 99.6KHz. -+ */ -+ if (of_property_read_u32(np, "ddc-i2c-scl-high-time-ns", -+ &hdmi->i2c->scl_high_ns)) -+ hdmi->i2c->scl_high_ns = 4708; -+ if (of_property_read_u32(np, "ddc-i2c-scl-low-time-ns", -+ &hdmi->i2c->scl_low_ns)) -+ hdmi->i2c->scl_low_ns = 4916; -+ } -+ -+ hdmi->bridge.driver_private = hdmi; -+ hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; -+#ifdef CONFIG_OF -+ hdmi->bridge.of_node = pdev->dev.of_node; -+#endif -+ -+ if (hdmi->phy.ops->setup_hpd) -+ hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); -+ -+ hdmi->connector.ycbcr_420_allowed = hdmi->plat_data->ycbcr_420_allowed; -+ -+ audio.hdmi = hdmi; -+ audio.eld = hdmi->connector.eld; -+ audio.write = hdmi_writel; -+ audio.read = hdmi_readl; -+ audio.mod = hdmi_modb; -+ hdmi->enable_audio = dw_hdmi_i2s_audio_enable; -+ hdmi->disable_audio = dw_hdmi_i2s_audio_disable; -+ -+ memset(&pdevinfo, 0, sizeof(pdevinfo)); -+ pdevinfo.parent = dev; -+ pdevinfo.id = PLATFORM_DEVID_AUTO; -+ pdevinfo.name = "dw-hdmi-qp-i2s-audio"; -+ pdevinfo.data = &audio; -+ pdevinfo.size_data = sizeof(audio); -+ pdevinfo.dma_mask = DMA_BIT_MASK(32); -+ hdmi->audio = platform_device_register_full(&pdevinfo); -+ -+ hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, dw_hdmi_cable); -+ if (IS_ERR(hdmi->extcon)) { -+ dev_err(hdmi->dev, "allocate extcon failed\n"); -+ ret = PTR_ERR(hdmi->extcon); -+ goto err_res; -+ } -+ -+ ret = devm_extcon_dev_register(hdmi->dev, hdmi->extcon); -+ if (ret) { -+ dev_err(hdmi->dev, "failed to register extcon: %d\n", ret); -+ goto err_res; -+ } -+ -+ ret = extcon_set_property_capability(hdmi->extcon, EXTCON_DISP_HDMI, -+ EXTCON_PROP_DISP_HPD); -+ if (ret) { -+ dev_err(hdmi->dev, -+ "failed to set USB property capability: %d\n", ret); -+ goto err_res; -+ } -+ -+ cec.hdmi = hdmi; -+ cec.ops = &dw_hdmi_qp_cec_ops; -+ pdevinfo.name = "dw-hdmi-qp-cec"; -+ pdevinfo.data = &cec; -+ pdevinfo.size_data = sizeof(cec); -+ pdevinfo.dma_mask = 0; -+ hdmi->cec = platform_device_register_full(&pdevinfo); -+ -+ /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */ -+ if (hdmi->i2c) -+ dw_hdmi_i2c_init(hdmi); -+ -+ init_completion(&hdmi->flt_cmp); -+ init_completion(&hdmi->earc_cmp); -+ -+ if (of_property_read_bool(np, "scramble-low-rates")) -+ hdmi->scramble_low_rates = true; -+ -+ dw_hdmi_register_debugfs(dev, hdmi); -+ -+ return hdmi; -+ -+err_res: -+ if (hdmi->i2c) -+ i2c_del_adapter(&hdmi->i2c->adap); -+ else -+ i2c_put_adapter(hdmi->ddc); -+ -+ return ERR_PTR(ret); -+} -+ -+static void __dw_hdmi_remove(struct dw_hdmi_qp *hdmi) -+{ -+ if (hdmi->avp_irq) -+ disable_irq(hdmi->avp_irq); -+ -+ if (hdmi->main_irq) -+ disable_irq(hdmi->main_irq); -+ -+ if (hdmi->earc_irq) -+ disable_irq(hdmi->earc_irq); -+ -+ debugfs_remove_recursive(hdmi->debugfs_dir); -+ -+ if (!hdmi->plat_data->first_screen) { -+ dw_hdmi_destroy_properties(hdmi); -+ hdmi->connector.funcs->destroy(&hdmi->connector); -+ } -+ -+ if (hdmi->audio && !IS_ERR(hdmi->audio)) -+ platform_device_unregister(hdmi->audio); -+ -+ if (hdmi->bridge.encoder && !hdmi->plat_data->first_screen) -+ hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder); -+ if (!IS_ERR(hdmi->cec)) -+ platform_device_unregister(hdmi->cec); -+ if (hdmi->i2c) -+ i2c_del_adapter(&hdmi->i2c->adap); -+ else -+ i2c_put_adapter(hdmi->ddc); -+} -+ -+/* ----------------------------------------------------------------------------- -+ * Bind/unbind API, used from platforms based on the component framework. -+ */ -+struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, -+ struct drm_encoder *encoder, -+ struct dw_hdmi_plat_data *plat_data) -+{ -+ struct dw_hdmi_qp *hdmi; -+ int ret; -+ -+ hdmi = __dw_hdmi_probe(pdev, plat_data); -+ if (IS_ERR(hdmi)) -+ return hdmi; -+ -+ if (!plat_data->first_screen) { -+ ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0); -+ if (ret) { -+ __dw_hdmi_remove(hdmi); -+ dev_err(hdmi->dev, "Failed to initialize bridge with drm\n"); -+ return ERR_PTR(ret); -+ } -+ -+ plat_data->connector = &hdmi->connector; -+ } -+ -+ if (plat_data->split_mode && !hdmi->plat_data->first_screen) { -+ struct dw_hdmi_qp *secondary = NULL; -+ -+ if (hdmi->plat_data->left) -+ secondary = hdmi->plat_data->left; -+ else if (hdmi->plat_data->right) -+ secondary = hdmi->plat_data->right; -+ -+ if (!secondary) -+ return ERR_PTR(-ENOMEM); -+ ret = drm_bridge_attach(encoder, &secondary->bridge, &hdmi->bridge, -+ DRM_BRIDGE_ATTACH_NO_CONNECTOR); -+ if (ret) -+ return ERR_PTR(ret); -+ } -+ -+ return hdmi; -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind); -+ -+void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi) -+{ -+ __dw_hdmi_remove(hdmi); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_unbind); -+ -+void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi) -+{ -+ if (!hdmi) { -+ dev_warn(dev, "Hdmi has not been initialized\n"); -+ return; -+ } -+ -+ mutex_lock(&hdmi->mutex); -+ -+ /* -+ * When system shutdown, hdmi should be disabled. -+ * When system suspend, dw_hdmi_qp_bridge_disable will disable hdmi first. -+ * To prevent duplicate operation, we should determine whether hdmi -+ * has been disabled. -+ */ -+ if (!hdmi->disabled) -+ hdmi->disabled = true; -+ mutex_unlock(&hdmi->mutex); -+ -+ if (hdmi->avp_irq) -+ disable_irq(hdmi->avp_irq); -+ -+ if (hdmi->main_irq) -+ disable_irq(hdmi->main_irq); -+ -+ if (hdmi->earc_irq) -+ disable_irq(hdmi->earc_irq); -+ -+ pinctrl_pm_select_sleep_state(dev); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_suspend); -+ -+void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi) -+{ -+ if (!hdmi) { -+ dev_warn(dev, "Hdmi has not been initialized\n"); -+ return; -+ } -+ -+ hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N); -+ hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N); -+ hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0); -+ -+ pinctrl_pm_select_default_state(dev); -+ -+ hdmi->cec_adap->ops->adap_enable(hdmi->cec_adap, true); -+ -+ mutex_lock(&hdmi->mutex); -+ if (hdmi->i2c) -+ dw_hdmi_i2c_init(hdmi); -+ if (hdmi->avp_irq) -+ enable_irq(hdmi->avp_irq); -+ -+ if (hdmi->main_irq) -+ enable_irq(hdmi->main_irq); -+ -+ if (hdmi->earc_irq) -+ enable_irq(hdmi->earc_irq); -+ -+ mutex_unlock(&hdmi->mutex); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_qp_resume); -+ -+MODULE_AUTHOR("Algea Cao "); -+MODULE_DESCRIPTION("DW HDMI QP transmitter driver"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("platform:dw-hdmi-qp"); -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h -new file mode 100644 -index 000000000000..4cac70f2d11d ---- /dev/null -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h -@@ -0,0 +1,831 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (C) Rockchip Electronics Co.Ltd -+ * Author: -+ * Algea Cao -+ */ -+#ifndef __DW_HDMI_QP_H__ -+#define __DW_HDMI_QP_H__ -+/* Main Unit Registers */ -+#define CORE_ID 0x0 -+#define VER_NUMBER 0x4 -+#define VER_TYPE 0x8 -+#define CONFIG_REG 0xc -+#define CONFIG_CEC BIT(28) -+#define CONFIG_AUD_UD BIT(23) -+#define CORE_TIMESTAMP_HHMM 0x14 -+#define CORE_TIMESTAMP_MMDD 0x18 -+#define CORE_TIMESTAMP_YYYY 0x1c -+/* Reset Manager Registers */ -+#define GLOBAL_SWRESET_REQUEST 0x40 -+#define EARCRX_CMDC_SWINIT_P BIT(27) -+#define AVP_DATAPATH_PACKET_AUDIO_SWINIT_P BIT(10) -+#define GLOBAL_SWDISABLE 0x44 -+#define CEC_SWDISABLE BIT(17) -+#define AVP_DATAPATH_PACKET_AUDIO_SWDISABLE BIT(10) -+#define AVP_DATAPATH_VIDEO_SWDISABLE BIT(6) -+#define RESET_MANAGER_CONFIG0 0x48 -+#define RESET_MANAGER_STATUS0 0x50 -+#define RESET_MANAGER_STATUS1 0x54 -+#define RESET_MANAGER_STATUS2 0x58 -+/* Timer Base Registers */ -+#define TIMER_BASE_CONFIG0 0x80 -+#define TIMER_BASE_STATUS0 0x84 -+/* CMU Registers */ -+#define CMU_CONFIG0 0xa0 -+#define CMU_CONFIG1 0xa4 -+#define CMU_CONFIG2 0xa8 -+#define CMU_CONFIG3 0xac -+#define CMU_STATUS 0xb0 -+#define DISPLAY_CLK_MONITOR 0x3f -+#define DISPLAY_CLK_LOCKED 0X15 -+#define EARC_BPCLK_OFF BIT(9) -+#define AUDCLK_OFF BIT(7) -+#define LINKQPCLK_OFF BIT(5) -+#define VIDQPCLK_OFF BIT(3) -+#define IPI_CLK_OFF BIT(1) -+#define CMU_IPI_CLK_FREQ 0xb4 -+#define CMU_VIDQPCLK_FREQ 0xb8 -+#define CMU_LINKQPCLK_FREQ 0xbc -+#define CMU_AUDQPCLK_FREQ 0xc0 -+#define CMU_EARC_BPCLK_FREQ 0xc4 -+/* I2CM Registers */ -+#define I2CM_SM_SCL_CONFIG0 0xe0 -+#define I2CM_FM_SCL_CONFIG0 0xe4 -+#define I2CM_CONFIG0 0xe8 -+#define I2CM_CONTROL0 0xec -+#define I2CM_STATUS0 0xf0 -+#define I2CM_INTERFACE_CONTROL0 0xf4 -+#define I2CM_ADDR 0xff000 -+#define I2CM_SLVADDR 0xfe0 -+#define I2CM_WR_MASK 0x1e -+#define I2CM_EXT_READ BIT(4) -+#define I2CM_SHORT_READ BIT(3) -+#define I2CM_FM_READ BIT(2) -+#define I2CM_FM_WRITE BIT(1) -+#define I2CM_FM_EN BIT(0) -+#define I2CM_INTERFACE_CONTROL1 0xf8 -+#define I2CM_SEG_PTR 0x7f80 -+#define I2CM_SEG_ADDR 0x7f -+#define I2CM_INTERFACE_WRDATA_0_3 0xfc -+#define I2CM_INTERFACE_WRDATA_4_7 0x100 -+#define I2CM_INTERFACE_WRDATA_8_11 0x104 -+#define I2CM_INTERFACE_WRDATA_12_15 0x108 -+#define I2CM_INTERFACE_RDDATA_0_3 0x10c -+#define I2CM_INTERFACE_RDDATA_4_7 0x110 -+#define I2CM_INTERFACE_RDDATA_8_11 0x114 -+#define I2CM_INTERFACE_RDDATA_12_15 0x118 -+/* SCDC Registers */ -+#define SCDC_CONFIG0 0x140 -+#define SCDC_I2C_FM_EN BIT(12) -+#define SCDC_UPD_FLAGS_AUTO_CLR BIT(6) -+#define SCDC_UPD_FLAGS_POLL_EN BIT(4) -+#define SCDC_CONTROL0 0x148 -+#define SCDC_STATUS0 0x150 -+#define STATUS_UPDATE BIT(0) -+#define FRL_START BIT(4) -+#define FLT_UPDATE BIT(5) -+/* FLT Registers */ -+#define FLT_CONFIG0 0x160 -+#define FLT_CONFIG1 0x164 -+#define FLT_CONFIG2 0x168 -+#define FLT_CONTROL0 0x170 -+/* Main Unit 2 Registers */ -+#define MAINUNIT_STATUS0 0x180 -+/* Video Interface Registers */ -+#define VIDEO_INTERFACE_CONFIG0 0x800 -+#define VIDEO_INTERFACE_CONFIG1 0x804 -+#define VIDEO_INTERFACE_CONFIG2 0x808 -+#define VIDEO_INTERFACE_CONTROL0 0x80c -+#define VIDEO_INTERFACE_STATUS0 0x814 -+/* Video Packing Registers */ -+#define VIDEO_PACKING_CONFIG0 0x81c -+/* Audio Interface Registers */ -+#define AUDIO_INTERFACE_CONFIG0 0x820 -+#define AUD_IF_SEL_MSK 0x3 -+#define AUD_IF_SPDIF 0x2 -+#define AUD_IF_I2S 0x1 -+#define AUD_IF_PAI 0x0 -+#define AUD_FIFO_INIT_ON_OVF_MSK BIT(2) -+#define AUD_FIFO_INIT_ON_OVF_EN BIT(2) -+#define I2S_LINES_EN_MSK GENMASK(7, 4) -+#define I2S_LINES_EN(x) BIT(x + 4) -+#define I2S_BPCUV_RCV_MSK BIT(12) -+#define I2S_BPCUV_RCV_EN BIT(12) -+#define I2S_BPCUV_RCV_DIS 0 -+#define SPDIF_LINES_EN GENMASK(19, 16) -+#define AUD_FORMAT_MSK GENMASK(26, 24) -+#define AUD_3DOBA (0x7 << 24) -+#define AUD_3DASP (0x6 << 24) -+#define AUD_MSOBA (0x5 << 24) -+#define AUD_MSASP (0x4 << 24) -+#define AUD_HBR (0x3 << 24) -+#define AUD_DST (0x2 << 24) -+#define AUD_OBA (0x1 << 24) -+#define AUD_ASP (0x0 << 24) -+#define AUDIO_INTERFACE_CONFIG1 0x824 -+#define AUDIO_INTERFACE_CONTROL0 0x82c -+#define AUDIO_FIFO_CLR_P BIT(0) -+#define AUDIO_INTERFACE_STATUS0 0x834 -+/* Frame Composer Registers */ -+#define FRAME_COMPOSER_CONFIG0 0x840 -+#define FRAME_COMPOSER_CONFIG1 0x844 -+#define FRAME_COMPOSER_CONFIG2 0x848 -+#define FRAME_COMPOSER_CONFIG3 0x84c -+#define FRAME_COMPOSER_CONFIG4 0x850 -+#define FRAME_COMPOSER_CONFIG5 0x854 -+#define FRAME_COMPOSER_CONFIG6 0x858 -+#define FRAME_COMPOSER_CONFIG7 0x85c -+#define FRAME_COMPOSER_CONFIG8 0x860 -+#define FRAME_COMPOSER_CONFIG9 0x864 -+#define FRAME_COMPOSER_CONTROL0 0x86c -+/* Video Monitor Registers */ -+#define VIDEO_MONITOR_CONFIG0 0x880 -+#define VIDEO_MONITOR_STATUS0 0x884 -+#define VIDEO_MONITOR_STATUS1 0x888 -+#define VIDEO_MONITOR_STATUS2 0x88c -+#define VIDEO_MONITOR_STATUS3 0x890 -+#define VIDEO_MONITOR_STATUS4 0x894 -+#define VIDEO_MONITOR_STATUS5 0x898 -+#define VIDEO_MONITOR_STATUS6 0x89c -+/* HDCP2 Logic Registers */ -+#define HDCP2LOGIC_CONFIG0 0x8e0 -+#define HDCP2_BYPASS BIT(0) -+#define HDCP2LOGIC_ESM_GPIO_IN 0x8e4 -+#define HDCP2LOGIC_ESM_GPIO_OUT 0x8e8 -+/* HDCP14 Registers */ -+#define HDCP14_CONFIG0 0x900 -+#define HDCP14_CONFIG1 0x904 -+#define HDCP14_CONFIG2 0x908 -+#define HDCP14_CONFIG3 0x90c -+#define HDCP14_KEY_SEED 0x914 -+#define HDCP14_KEY_H 0x918 -+#define HDCP14_KEY_L 0x91c -+#define HDCP14_KEY_STATUS 0x920 -+#define HDCP14_AKSV_H 0x924 -+#define HDCP14_AKSV_L 0x928 -+#define HDCP14_AN_H 0x92c -+#define HDCP14_AN_L 0x930 -+#define HDCP14_STATUS0 0x934 -+#define HDCP14_STATUS1 0x938 -+/* Scrambler Registers */ -+#define SCRAMB_CONFIG0 0x960 -+/* Video Configuration Registers */ -+#define LINK_CONFIG0 0x968 -+#define OPMODE_FRL_4LANES BIT(8) -+#define OPMODE_DVI BIT(4) -+#define OPMODE_FRL BIT(0) -+/* TMDS FIFO Registers */ -+#define TMDS_FIFO_CONFIG0 0x970 -+#define TMDS_FIFO_CONTROL0 0x974 -+/* FRL RSFEC Registers */ -+#define FRL_RSFEC_CONFIG0 0xa20 -+#define FRL_RSFEC_STATUS0 0xa30 -+/* FRL Packetizer Registers */ -+#define FRL_PKTZ_CONFIG0 0xa40 -+#define FRL_PKTZ_CONTROL0 0xa44 -+#define FRL_PKTZ_CONTROL1 0xa50 -+#define FRL_PKTZ_STATUS1 0xa54 -+/* Packet Scheduler Registers */ -+#define PKTSCHED_CONFIG0 0xa80 -+#define PKTSCHED_PRQUEUE0_CONFIG0 0xa84 -+#define PKTSCHED_PRQUEUE1_CONFIG0 0xa88 -+#define PKTSCHED_PRQUEUE2_CONFIG0 0xa8c -+#define PKTSCHED_PRQUEUE2_CONFIG1 0xa90 -+#define PKTSCHED_PRQUEUE2_CONFIG2 0xa94 -+#define PKTSCHED_PKT_CONFIG0 0xa98 -+#define PKTSCHED_PKT_CONFIG1 0xa9c -+#define PKTSCHED_DRMI_FIELDRATE BIT(13) -+#define PKTSCHED_AVI_FIELDRATE BIT(12) -+#define PKTSCHED_PKT_CONFIG2 0xaa0 -+#define PKTSCHED_PKT_CONFIG3 0xaa4 -+#define PKTSCHED_PKT_EN 0xaa8 -+#define PKTSCHED_DRMI_TX_EN BIT(17) -+#define PKTSCHED_AUDI_TX_EN BIT(15) -+#define PKTSCHED_AVI_TX_EN BIT(13) -+#define PKTSCHED_EMP_CVTEM_TX_EN BIT(10) -+#define PKTSCHED_AMD_TX_EN BIT(8) -+#define PKTSCHED_GCP_TX_EN BIT(3) -+#define PKTSCHED_AUDS_TX_EN BIT(2) -+#define PKTSCHED_ACR_TX_EN BIT(1) -+#define PKTSCHED_NULL_TX_EN BIT(0) -+#define PKTSCHED_PKT_CONTROL0 0xaac -+#define PKTSCHED_PKT_SEND 0xab0 -+#define PKTSCHED_PKT_STATUS0 0xab4 -+#define PKTSCHED_PKT_STATUS1 0xab8 -+#define PKT_NULL_CONTENTS0 0xb00 -+#define PKT_NULL_CONTENTS1 0xb04 -+#define PKT_NULL_CONTENTS2 0xb08 -+#define PKT_NULL_CONTENTS3 0xb0c -+#define PKT_NULL_CONTENTS4 0xb10 -+#define PKT_NULL_CONTENTS5 0xb14 -+#define PKT_NULL_CONTENTS6 0xb18 -+#define PKT_NULL_CONTENTS7 0xb1c -+#define PKT_ACP_CONTENTS0 0xb20 -+#define PKT_ACP_CONTENTS1 0xb24 -+#define PKT_ACP_CONTENTS2 0xb28 -+#define PKT_ACP_CONTENTS3 0xb2c -+#define PKT_ACP_CONTENTS4 0xb30 -+#define PKT_ACP_CONTENTS5 0xb34 -+#define PKT_ACP_CONTENTS6 0xb38 -+#define PKT_ACP_CONTENTS7 0xb3c -+#define PKT_ISRC1_CONTENTS0 0xb40 -+#define PKT_ISRC1_CONTENTS1 0xb44 -+#define PKT_ISRC1_CONTENTS2 0xb48 -+#define PKT_ISRC1_CONTENTS3 0xb4c -+#define PKT_ISRC1_CONTENTS4 0xb50 -+#define PKT_ISRC1_CONTENTS5 0xb54 -+#define PKT_ISRC1_CONTENTS6 0xb58 -+#define PKT_ISRC1_CONTENTS7 0xb5c -+#define PKT_ISRC2_CONTENTS0 0xb60 -+#define PKT_ISRC2_CONTENTS1 0xb64 -+#define PKT_ISRC2_CONTENTS2 0xb68 -+#define PKT_ISRC2_CONTENTS3 0xb6c -+#define PKT_ISRC2_CONTENTS4 0xb70 -+#define PKT_ISRC2_CONTENTS5 0xb74 -+#define PKT_ISRC2_CONTENTS6 0xb78 -+#define PKT_ISRC2_CONTENTS7 0xb7c -+#define PKT_GMD_CONTENTS0 0xb80 -+#define PKT_GMD_CONTENTS1 0xb84 -+#define PKT_GMD_CONTENTS2 0xb88 -+#define PKT_GMD_CONTENTS3 0xb8c -+#define PKT_GMD_CONTENTS4 0xb90 -+#define PKT_GMD_CONTENTS5 0xb94 -+#define PKT_GMD_CONTENTS6 0xb98 -+#define PKT_GMD_CONTENTS7 0xb9c -+#define PKT_AMD_CONTENTS0 0xba0 -+#define PKT_AMD_CONTENTS1 0xba4 -+#define PKT_AMD_CONTENTS2 0xba8 -+#define PKT_AMD_CONTENTS3 0xbac -+#define PKT_AMD_CONTENTS4 0xbb0 -+#define PKT_AMD_CONTENTS5 0xbb4 -+#define PKT_AMD_CONTENTS6 0xbb8 -+#define PKT_AMD_CONTENTS7 0xbbc -+#define PKT_VSI_CONTENTS0 0xbc0 -+#define PKT_VSI_CONTENTS1 0xbc4 -+#define PKT_VSI_CONTENTS2 0xbc8 -+#define PKT_VSI_CONTENTS3 0xbcc -+#define PKT_VSI_CONTENTS4 0xbd0 -+#define PKT_VSI_CONTENTS5 0xbd4 -+#define PKT_VSI_CONTENTS6 0xbd8 -+#define PKT_VSI_CONTENTS7 0xbdc -+#define PKT_AVI_CONTENTS0 0xbe0 -+#define HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT BIT(4) -+#define HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR 0x04 -+#define HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR 0x08 -+#define HDMI_FC_AVICONF2_IT_CONTENT_VALID 0x80 -+#define PKT_AVI_CONTENTS1 0xbe4 -+#define PKT_AVI_CONTENTS2 0xbe8 -+#define PKT_AVI_CONTENTS3 0xbec -+#define PKT_AVI_CONTENTS4 0xbf0 -+#define PKT_AVI_CONTENTS5 0xbf4 -+#define PKT_AVI_CONTENTS6 0xbf8 -+#define PKT_AVI_CONTENTS7 0xbfc -+#define PKT_SPDI_CONTENTS0 0xc00 -+#define PKT_SPDI_CONTENTS1 0xc04 -+#define PKT_SPDI_CONTENTS2 0xc08 -+#define PKT_SPDI_CONTENTS3 0xc0c -+#define PKT_SPDI_CONTENTS4 0xc10 -+#define PKT_SPDI_CONTENTS5 0xc14 -+#define PKT_SPDI_CONTENTS6 0xc18 -+#define PKT_SPDI_CONTENTS7 0xc1c -+#define PKT_AUDI_CONTENTS0 0xc20 -+#define PKT_AUDI_CONTENTS1 0xc24 -+#define PKT_AUDI_CONTENTS2 0xc28 -+#define PKT_AUDI_CONTENTS3 0xc2c -+#define PKT_AUDI_CONTENTS4 0xc30 -+#define PKT_AUDI_CONTENTS5 0xc34 -+#define PKT_AUDI_CONTENTS6 0xc38 -+#define PKT_AUDI_CONTENTS7 0xc3c -+#define PKT_NVI_CONTENTS0 0xc40 -+#define PKT_NVI_CONTENTS1 0xc44 -+#define PKT_NVI_CONTENTS2 0xc48 -+#define PKT_NVI_CONTENTS3 0xc4c -+#define PKT_NVI_CONTENTS4 0xc50 -+#define PKT_NVI_CONTENTS5 0xc54 -+#define PKT_NVI_CONTENTS6 0xc58 -+#define PKT_NVI_CONTENTS7 0xc5c -+#define PKT_DRMI_CONTENTS0 0xc60 -+#define PKT_DRMI_CONTENTS1 0xc64 -+#define PKT_DRMI_CONTENTS2 0xc68 -+#define PKT_DRMI_CONTENTS3 0xc6c -+#define PKT_DRMI_CONTENTS4 0xc70 -+#define PKT_DRMI_CONTENTS5 0xc74 -+#define PKT_DRMI_CONTENTS6 0xc78 -+#define PKT_DRMI_CONTENTS7 0xc7c -+#define PKT_GHDMI1_CONTENTS0 0xc80 -+#define PKT_GHDMI1_CONTENTS1 0xc84 -+#define PKT_GHDMI1_CONTENTS2 0xc88 -+#define PKT_GHDMI1_CONTENTS3 0xc8c -+#define PKT_GHDMI1_CONTENTS4 0xc90 -+#define PKT_GHDMI1_CONTENTS5 0xc94 -+#define PKT_GHDMI1_CONTENTS6 0xc98 -+#define PKT_GHDMI1_CONTENTS7 0xc9c -+#define PKT_GHDMI2_CONTENTS0 0xca0 -+#define PKT_GHDMI2_CONTENTS1 0xca4 -+#define PKT_GHDMI2_CONTENTS2 0xca8 -+#define PKT_GHDMI2_CONTENTS3 0xcac -+#define PKT_GHDMI2_CONTENTS4 0xcb0 -+#define PKT_GHDMI2_CONTENTS5 0xcb4 -+#define PKT_GHDMI2_CONTENTS6 0xcb8 -+#define PKT_GHDMI2_CONTENTS7 0xcbc -+/* EMP Packetizer Registers */ -+#define PKT_EMP_CONFIG0 0xce0 -+#define PKT_EMP_CONTROL0 0xcec -+#define PKT_EMP_CONTROL1 0xcf0 -+#define PKT_EMP_CONTROL2 0xcf4 -+#define PKT_EMP_VTEM_CONTENTS0 0xd00 -+#define PKT_EMP_VTEM_CONTENTS1 0xd04 -+#define PKT_EMP_VTEM_CONTENTS2 0xd08 -+#define PKT_EMP_VTEM_CONTENTS3 0xd0c -+#define PKT_EMP_VTEM_CONTENTS4 0xd10 -+#define PKT_EMP_VTEM_CONTENTS5 0xd14 -+#define PKT_EMP_VTEM_CONTENTS6 0xd18 -+#define PKT_EMP_VTEM_CONTENTS7 0xd1c -+#define PKT0_EMP_CVTEM_CONTENTS0 0xd20 -+#define PKT0_EMP_CVTEM_CONTENTS1 0xd24 -+#define PKT0_EMP_CVTEM_CONTENTS2 0xd28 -+#define PKT0_EMP_CVTEM_CONTENTS3 0xd2c -+#define PKT0_EMP_CVTEM_CONTENTS4 0xd30 -+#define PKT0_EMP_CVTEM_CONTENTS5 0xd34 -+#define PKT0_EMP_CVTEM_CONTENTS6 0xd38 -+#define PKT0_EMP_CVTEM_CONTENTS7 0xd3c -+#define PKT1_EMP_CVTEM_CONTENTS0 0xd40 -+#define PKT1_EMP_CVTEM_CONTENTS1 0xd44 -+#define PKT1_EMP_CVTEM_CONTENTS2 0xd48 -+#define PKT1_EMP_CVTEM_CONTENTS3 0xd4c -+#define PKT1_EMP_CVTEM_CONTENTS4 0xd50 -+#define PKT1_EMP_CVTEM_CONTENTS5 0xd54 -+#define PKT1_EMP_CVTEM_CONTENTS6 0xd58 -+#define PKT1_EMP_CVTEM_CONTENTS7 0xd5c -+#define PKT2_EMP_CVTEM_CONTENTS0 0xd60 -+#define PKT2_EMP_CVTEM_CONTENTS1 0xd64 -+#define PKT2_EMP_CVTEM_CONTENTS2 0xd68 -+#define PKT2_EMP_CVTEM_CONTENTS3 0xd6c -+#define PKT2_EMP_CVTEM_CONTENTS4 0xd70 -+#define PKT2_EMP_CVTEM_CONTENTS5 0xd74 -+#define PKT2_EMP_CVTEM_CONTENTS6 0xd78 -+#define PKT2_EMP_CVTEM_CONTENTS7 0xd7c -+#define PKT3_EMP_CVTEM_CONTENTS0 0xd80 -+#define PKT3_EMP_CVTEM_CONTENTS1 0xd84 -+#define PKT3_EMP_CVTEM_CONTENTS2 0xd88 -+#define PKT3_EMP_CVTEM_CONTENTS3 0xd8c -+#define PKT3_EMP_CVTEM_CONTENTS4 0xd90 -+#define PKT3_EMP_CVTEM_CONTENTS5 0xd94 -+#define PKT3_EMP_CVTEM_CONTENTS6 0xd98 -+#define PKT3_EMP_CVTEM_CONTENTS7 0xd9c -+#define PKT4_EMP_CVTEM_CONTENTS0 0xda0 -+#define PKT4_EMP_CVTEM_CONTENTS1 0xda4 -+#define PKT4_EMP_CVTEM_CONTENTS2 0xda8 -+#define PKT4_EMP_CVTEM_CONTENTS3 0xdac -+#define PKT4_EMP_CVTEM_CONTENTS4 0xdb0 -+#define PKT4_EMP_CVTEM_CONTENTS5 0xdb4 -+#define PKT4_EMP_CVTEM_CONTENTS6 0xdb8 -+#define PKT4_EMP_CVTEM_CONTENTS7 0xdbc -+#define PKT5_EMP_CVTEM_CONTENTS0 0xdc0 -+#define PKT5_EMP_CVTEM_CONTENTS1 0xdc4 -+#define PKT5_EMP_CVTEM_CONTENTS2 0xdc8 -+#define PKT5_EMP_CVTEM_CONTENTS3 0xdcc -+#define PKT5_EMP_CVTEM_CONTENTS4 0xdd0 -+#define PKT5_EMP_CVTEM_CONTENTS5 0xdd4 -+#define PKT5_EMP_CVTEM_CONTENTS6 0xdd8 -+#define PKT5_EMP_CVTEM_CONTENTS7 0xddc -+/* Audio Packetizer Registers */ -+#define AUDPKT_CONTROL0 0xe20 -+#define AUDPKT_PBIT_FORCE_EN_MASK BIT(12) -+#define AUDPKT_PBIT_FORCE_EN BIT(12) -+#define AUDPKT_CHSTATUS_OVR_EN_MASK BIT(0) -+#define AUDPKT_CHSTATUS_OVR_EN BIT(0) -+#define AUDPKT_CONTROL1 0xe24 -+#define AUDPKT_ACR_CONTROL0 0xe40 -+#define AUDPKT_ACR_N_VALUE 0xfffff -+#define AUDPKT_ACR_CONTROL1 0xe44 -+#define AUDPKT_ACR_CTS_OVR_VAL_MSK GENMASK(23, 4) -+#define AUDPKT_ACR_CTS_OVR_VAL(x) ((x) << 4) -+#define AUDPKT_ACR_CTS_OVR_EN_MSK BIT(1) -+#define AUDPKT_ACR_CTS_OVR_EN BIT(1) -+#define AUDPKT_ACR_STATUS0 0xe4c -+#define AUDPKT_CHSTATUS_OVR0 0xe60 -+#define AUDPKT_CHSTATUS_OVR1 0xe64 -+/* IEC60958 Byte 3: Sampleing frenuency Bits 24 to 27 */ -+#define AUDPKT_CHSTATUS_SR_MASK GENMASK(3, 0) -+#define AUDPKT_CHSTATUS_SR_22050 0x4 -+#define AUDPKT_CHSTATUS_SR_24000 0x6 -+#define AUDPKT_CHSTATUS_SR_32000 0x3 -+#define AUDPKT_CHSTATUS_SR_44100 0x0 -+#define AUDPKT_CHSTATUS_SR_48000 0x2 -+#define AUDPKT_CHSTATUS_SR_88200 0x8 -+#define AUDPKT_CHSTATUS_SR_96000 0xa -+#define AUDPKT_CHSTATUS_SR_176400 0xc -+#define AUDPKT_CHSTATUS_SR_192000 0xe -+#define AUDPKT_CHSTATUS_SR_768000 0x9 -+#define AUDPKT_CHSTATUS_SR_NOT_INDICATED 0x1 -+/* IEC60958 Byte 4: Original Sampleing frenuency Bits 36 to 39 */ -+#define AUDPKT_CHSTATUS_0SR_MASK GENMASK(15, 12) -+#define AUDPKT_CHSTATUS_OSR_8000 0x6 -+#define AUDPKT_CHSTATUS_OSR_11025 0xa -+#define AUDPKT_CHSTATUS_OSR_12000 0x2 -+#define AUDPKT_CHSTATUS_OSR_16000 0x8 -+#define AUDPKT_CHSTATUS_OSR_22050 0xb -+#define AUDPKT_CHSTATUS_OSR_24000 0x9 -+#define AUDPKT_CHSTATUS_OSR_32000 0xc -+#define AUDPKT_CHSTATUS_OSR_44100 0xf -+#define AUDPKT_CHSTATUS_OSR_48000 0xd -+#define AUDPKT_CHSTATUS_OSR_88200 0x7 -+#define AUDPKT_CHSTATUS_OSR_96000 0x5 -+#define AUDPKT_CHSTATUS_OSR_176400 0x3 -+#define AUDPKT_CHSTATUS_OSR_192000 0x1 -+#define AUDPKT_CHSTATUS_OSR_NOT_INDICATED 0x0 -+#define AUDPKT_CHSTATUS_OVR2 0xe68 -+#define AUDPKT_CHSTATUS_OVR3 0xe6c -+#define AUDPKT_CHSTATUS_OVR4 0xe70 -+#define AUDPKT_CHSTATUS_OVR5 0xe74 -+#define AUDPKT_CHSTATUS_OVR6 0xe78 -+#define AUDPKT_CHSTATUS_OVR7 0xe7c -+#define AUDPKT_CHSTATUS_OVR8 0xe80 -+#define AUDPKT_CHSTATUS_OVR9 0xe84 -+#define AUDPKT_CHSTATUS_OVR10 0xe88 -+#define AUDPKT_CHSTATUS_OVR11 0xe8c -+#define AUDPKT_CHSTATUS_OVR12 0xe90 -+#define AUDPKT_CHSTATUS_OVR13 0xe94 -+#define AUDPKT_CHSTATUS_OVR14 0xe98 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC0 0xea0 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC1 0xea4 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC2 0xea8 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC3 0xeac -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC4 0xeb0 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC5 0xeb4 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC6 0xeb8 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC7 0xebc -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC8 0xec0 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC9 0xec4 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC10 0xec8 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC11 0xecc -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC12 0xed0 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC13 0xed4 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC14 0xed8 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC15 0xedc -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC16 0xee0 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC17 0xee4 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC18 0xee8 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC19 0xeec -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC20 0xef0 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC21 0xef4 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC22 0xef8 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC23 0xefc -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC24 0xf00 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC25 0xf04 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC26 0xf08 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC27 0xf0c -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC28 0xf10 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC29 0xf14 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC30 0xf18 -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC31 0xf1c -+#define AUDPKT_USRDATA_OVR_MSG_GENERIC32 0xf20 -+#define AUDPKT_VBIT_OVR0 0xf24 -+/* CEC Registers */ -+#define CEC_TX_CONTROL 0x1000 -+#define CEC_STATUS 0x1004 -+#define CEC_CONFIG 0x1008 -+#define CEC_ADDR 0x100c -+#define CEC_TX_COUNT 0x1020 -+#define CEC_TX_DATA3_0 0x1024 -+#define CEC_TX_DATA7_4 0x1028 -+#define CEC_TX_DATA11_8 0x102c -+#define CEC_TX_DATA15_12 0x1030 -+#define CEC_RX_COUNT_STATUS 0x1040 -+#define CEC_RX_DATA3_0 0x1044 -+#define CEC_RX_DATA7_4 0x1048 -+#define CEC_RX_DATA11_8 0x104c -+#define CEC_RX_DATA15_12 0x1050 -+#define CEC_LOCK_CONTROL 0x1054 -+#define CEC_RXQUAL_BITTIME_CONFIG 0x1060 -+#define CEC_RX_BITTIME_CONFIG 0x1064 -+#define CEC_TX_BITTIME_CONFIG 0x1068 -+/* eARC RX CMDC Registers */ -+#define EARCRX_CMDC_CONFIG0 0x1800 -+#define EARCRX_XACTREAD_STOP_CFG BIT(26) -+#define EARCRX_XACTREAD_RETRY_CFG BIT(25) -+#define EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 BIT(24) -+#define EARCRX_CMDC_XACT_RESTART_EN BIT(18) -+#define EARCRX_CMDC_CONFIG1 0x1804 -+#define EARCRX_CMDC_CONTROL 0x1808 -+#define EARCRX_CMDC_HEARTBEAT_LOSS_EN BIT(4) -+#define EARCRX_CMDC_DISCOVERY_EN BIT(3) -+#define EARCRX_CONNECTOR_HPD BIT(1) -+#define EARCRX_CMDC_WHITELIST0_CONFIG 0x180c -+#define EARCRX_CMDC_WHITELIST1_CONFIG 0x1810 -+#define EARCRX_CMDC_WHITELIST2_CONFIG 0x1814 -+#define EARCRX_CMDC_WHITELIST3_CONFIG 0x1818 -+#define EARCRX_CMDC_STATUS 0x181c -+#define EARCRX_CMDC_XACT_INFO 0x1820 -+#define EARCRX_CMDC_XACT_ACTION 0x1824 -+#define EARCRX_CMDC_HEARTBEAT_RXSTAT_SE 0x1828 -+#define EARCRX_CMDC_HEARTBEAT_STATUS 0x182c -+#define EARCRX_CMDC_XACT_WR0 0x1840 -+#define EARCRX_CMDC_XACT_WR1 0x1844 -+#define EARCRX_CMDC_XACT_WR2 0x1848 -+#define EARCRX_CMDC_XACT_WR3 0x184c -+#define EARCRX_CMDC_XACT_WR4 0x1850 -+#define EARCRX_CMDC_XACT_WR5 0x1854 -+#define EARCRX_CMDC_XACT_WR6 0x1858 -+#define EARCRX_CMDC_XACT_WR7 0x185c -+#define EARCRX_CMDC_XACT_WR8 0x1860 -+#define EARCRX_CMDC_XACT_WR9 0x1864 -+#define EARCRX_CMDC_XACT_WR10 0x1868 -+#define EARCRX_CMDC_XACT_WR11 0x186c -+#define EARCRX_CMDC_XACT_WR12 0x1870 -+#define EARCRX_CMDC_XACT_WR13 0x1874 -+#define EARCRX_CMDC_XACT_WR14 0x1878 -+#define EARCRX_CMDC_XACT_WR15 0x187c -+#define EARCRX_CMDC_XACT_WR16 0x1880 -+#define EARCRX_CMDC_XACT_WR17 0x1884 -+#define EARCRX_CMDC_XACT_WR18 0x1888 -+#define EARCRX_CMDC_XACT_WR19 0x188c -+#define EARCRX_CMDC_XACT_WR20 0x1890 -+#define EARCRX_CMDC_XACT_WR21 0x1894 -+#define EARCRX_CMDC_XACT_WR22 0x1898 -+#define EARCRX_CMDC_XACT_WR23 0x189c -+#define EARCRX_CMDC_XACT_WR24 0x18a0 -+#define EARCRX_CMDC_XACT_WR25 0x18a4 -+#define EARCRX_CMDC_XACT_WR26 0x18a8 -+#define EARCRX_CMDC_XACT_WR27 0x18ac -+#define EARCRX_CMDC_XACT_WR28 0x18b0 -+#define EARCRX_CMDC_XACT_WR29 0x18b4 -+#define EARCRX_CMDC_XACT_WR30 0x18b8 -+#define EARCRX_CMDC_XACT_WR31 0x18bc -+#define EARCRX_CMDC_XACT_WR32 0x18c0 -+#define EARCRX_CMDC_XACT_WR33 0x18c4 -+#define EARCRX_CMDC_XACT_WR34 0x18c8 -+#define EARCRX_CMDC_XACT_WR35 0x18cc -+#define EARCRX_CMDC_XACT_WR36 0x18d0 -+#define EARCRX_CMDC_XACT_WR37 0x18d4 -+#define EARCRX_CMDC_XACT_WR38 0x18d8 -+#define EARCRX_CMDC_XACT_WR39 0x18dc -+#define EARCRX_CMDC_XACT_WR40 0x18e0 -+#define EARCRX_CMDC_XACT_WR41 0x18e4 -+#define EARCRX_CMDC_XACT_WR42 0x18e8 -+#define EARCRX_CMDC_XACT_WR43 0x18ec -+#define EARCRX_CMDC_XACT_WR44 0x18f0 -+#define EARCRX_CMDC_XACT_WR45 0x18f4 -+#define EARCRX_CMDC_XACT_WR46 0x18f8 -+#define EARCRX_CMDC_XACT_WR47 0x18fc -+#define EARCRX_CMDC_XACT_WR48 0x1900 -+#define EARCRX_CMDC_XACT_WR49 0x1904 -+#define EARCRX_CMDC_XACT_WR50 0x1908 -+#define EARCRX_CMDC_XACT_WR51 0x190c -+#define EARCRX_CMDC_XACT_WR52 0x1910 -+#define EARCRX_CMDC_XACT_WR53 0x1914 -+#define EARCRX_CMDC_XACT_WR54 0x1918 -+#define EARCRX_CMDC_XACT_WR55 0x191c -+#define EARCRX_CMDC_XACT_WR56 0x1920 -+#define EARCRX_CMDC_XACT_WR57 0x1924 -+#define EARCRX_CMDC_XACT_WR58 0x1928 -+#define EARCRX_CMDC_XACT_WR59 0x192c -+#define EARCRX_CMDC_XACT_WR60 0x1930 -+#define EARCRX_CMDC_XACT_WR61 0x1934 -+#define EARCRX_CMDC_XACT_WR62 0x1938 -+#define EARCRX_CMDC_XACT_WR63 0x193c -+#define EARCRX_CMDC_XACT_WR64 0x1940 -+#define EARCRX_CMDC_XACT_RD0 0x1960 -+#define EARCRX_CMDC_XACT_RD1 0x1964 -+#define EARCRX_CMDC_XACT_RD2 0x1968 -+#define EARCRX_CMDC_XACT_RD3 0x196c -+#define EARCRX_CMDC_XACT_RD4 0x1970 -+#define EARCRX_CMDC_XACT_RD5 0x1974 -+#define EARCRX_CMDC_XACT_RD6 0x1978 -+#define EARCRX_CMDC_XACT_RD7 0x197c -+#define EARCRX_CMDC_XACT_RD8 0x1980 -+#define EARCRX_CMDC_XACT_RD9 0x1984 -+#define EARCRX_CMDC_XACT_RD10 0x1988 -+#define EARCRX_CMDC_XACT_RD11 0x198c -+#define EARCRX_CMDC_XACT_RD12 0x1990 -+#define EARCRX_CMDC_XACT_RD13 0x1994 -+#define EARCRX_CMDC_XACT_RD14 0x1998 -+#define EARCRX_CMDC_XACT_RD15 0x199c -+#define EARCRX_CMDC_XACT_RD16 0x19a0 -+#define EARCRX_CMDC_XACT_RD17 0x19a4 -+#define EARCRX_CMDC_XACT_RD18 0x19a8 -+#define EARCRX_CMDC_XACT_RD19 0x19ac -+#define EARCRX_CMDC_XACT_RD20 0x19b0 -+#define EARCRX_CMDC_XACT_RD21 0x19b4 -+#define EARCRX_CMDC_XACT_RD22 0x19b8 -+#define EARCRX_CMDC_XACT_RD23 0x19bc -+#define EARCRX_CMDC_XACT_RD24 0x19c0 -+#define EARCRX_CMDC_XACT_RD25 0x19c4 -+#define EARCRX_CMDC_XACT_RD26 0x19c8 -+#define EARCRX_CMDC_XACT_RD27 0x19cc -+#define EARCRX_CMDC_XACT_RD28 0x19d0 -+#define EARCRX_CMDC_XACT_RD29 0x19d4 -+#define EARCRX_CMDC_XACT_RD30 0x19d8 -+#define EARCRX_CMDC_XACT_RD31 0x19dc -+#define EARCRX_CMDC_XACT_RD32 0x19e0 -+#define EARCRX_CMDC_XACT_RD33 0x19e4 -+#define EARCRX_CMDC_XACT_RD34 0x19e8 -+#define EARCRX_CMDC_XACT_RD35 0x19ec -+#define EARCRX_CMDC_XACT_RD36 0x19f0 -+#define EARCRX_CMDC_XACT_RD37 0x19f4 -+#define EARCRX_CMDC_XACT_RD38 0x19f8 -+#define EARCRX_CMDC_XACT_RD39 0x19fc -+#define EARCRX_CMDC_XACT_RD40 0x1a00 -+#define EARCRX_CMDC_XACT_RD41 0x1a04 -+#define EARCRX_CMDC_XACT_RD42 0x1a08 -+#define EARCRX_CMDC_XACT_RD43 0x1a0c -+#define EARCRX_CMDC_XACT_RD44 0x1a10 -+#define EARCRX_CMDC_XACT_RD45 0x1a14 -+#define EARCRX_CMDC_XACT_RD46 0x1a18 -+#define EARCRX_CMDC_XACT_RD47 0x1a1c -+#define EARCRX_CMDC_XACT_RD48 0x1a20 -+#define EARCRX_CMDC_XACT_RD49 0x1a24 -+#define EARCRX_CMDC_XACT_RD50 0x1a28 -+#define EARCRX_CMDC_XACT_RD51 0x1a2c -+#define EARCRX_CMDC_XACT_RD52 0x1a30 -+#define EARCRX_CMDC_XACT_RD53 0x1a34 -+#define EARCRX_CMDC_XACT_RD54 0x1a38 -+#define EARCRX_CMDC_XACT_RD55 0x1a3c -+#define EARCRX_CMDC_XACT_RD56 0x1a40 -+#define EARCRX_CMDC_XACT_RD57 0x1a44 -+#define EARCRX_CMDC_XACT_RD58 0x1a48 -+#define EARCRX_CMDC_XACT_RD59 0x1a4c -+#define EARCRX_CMDC_XACT_RD60 0x1a50 -+#define EARCRX_CMDC_XACT_RD61 0x1a54 -+#define EARCRX_CMDC_XACT_RD62 0x1a58 -+#define EARCRX_CMDC_XACT_RD63 0x1a5c -+#define EARCRX_CMDC_XACT_RD64 0x1a60 -+#define EARCRX_CMDC_SYNC_CONFIG 0x1b00 -+/* eARC RX DMAC Registers */ -+#define EARCRX_DMAC_PHY_CONTROL 0x1c00 -+#define EARCRX_DMAC_CONFIG 0x1c08 -+#define EARCRX_DMAC_CONTROL0 0x1c0c -+#define EARCRX_DMAC_AUDIO_EN BIT(1) -+#define EARCRX_DMAC_EN BIT(0) -+#define EARCRX_DMAC_CONTROL1 0x1c10 -+#define EARCRX_DMAC_STATUS 0x1c14 -+#define EARCRX_DMAC_CHSTATUS0 0x1c18 -+#define EARCRX_DMAC_CHSTATUS1 0x1c1c -+#define EARCRX_DMAC_CHSTATUS2 0x1c20 -+#define EARCRX_DMAC_CHSTATUS3 0x1c24 -+#define EARCRX_DMAC_CHSTATUS4 0x1c28 -+#define EARCRX_DMAC_CHSTATUS5 0x1c2c -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC0 0x1c30 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC1 0x1c34 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC2 0x1c38 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC3 0x1c3c -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC4 0x1c40 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC5 0x1c44 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC6 0x1c48 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC7 0x1c4c -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC8 0x1c50 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC9 0x1c54 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC10 0x1c58 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC11 0x1c5c -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT0 0x1c60 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT1 0x1c64 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT2 0x1c68 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT3 0x1c6c -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT4 0x1c70 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT5 0x1c74 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT6 0x1c78 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT7 0x1c7c -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT8 0x1c80 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT9 0x1c84 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT10 0x1c88 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT11 0x1c8c -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT0 0x1c90 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT1 0x1c94 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT2 0x1c98 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT3 0x1c9c -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT4 0x1ca0 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT5 0x1ca4 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT6 0x1ca8 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT7 0x1cac -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT8 0x1cb0 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT9 0x1cb4 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT10 0x1cb8 -+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT11 0x1cbc -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC0 0x1cc0 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC1 0x1cc4 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC2 0x1cc8 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC3 0x1ccc -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC4 0x1cd0 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC5 0x1cd4 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC6 0x1cd8 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC7 0x1cdc -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC8 0x1ce0 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC9 0x1ce4 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC10 0x1ce8 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC11 0x1cec -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC12 0x1cf0 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC13 0x1cf4 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC14 0x1cf8 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC15 0x1cfc -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC16 0x1d00 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC17 0x1d04 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC18 0x1d08 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC19 0x1d0c -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC20 0x1d10 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC21 0x1d14 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC22 0x1d18 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC23 0x1d1c -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC24 0x1d20 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC25 0x1d24 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC26 0x1d28 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC27 0x1d2c -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC28 0x1d30 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC29 0x1d34 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC30 0x1d38 -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC31 0x1d3c -+#define EARCRX_DMAC_USRDATA_MSG_GENERIC32 0x1d40 -+#define EARCRX_DMAC_CHSTATUS_STREAMER0 0x1d44 -+#define EARCRX_DMAC_CHSTATUS_STREAMER1 0x1d48 -+#define EARCRX_DMAC_CHSTATUS_STREAMER2 0x1d4c -+#define EARCRX_DMAC_CHSTATUS_STREAMER3 0x1d50 -+#define EARCRX_DMAC_CHSTATUS_STREAMER4 0x1d54 -+#define EARCRX_DMAC_CHSTATUS_STREAMER5 0x1d58 -+#define EARCRX_DMAC_CHSTATUS_STREAMER6 0x1d5c -+#define EARCRX_DMAC_CHSTATUS_STREAMER7 0x1d60 -+#define EARCRX_DMAC_CHSTATUS_STREAMER8 0x1d64 -+#define EARCRX_DMAC_CHSTATUS_STREAMER9 0x1d68 -+#define EARCRX_DMAC_CHSTATUS_STREAMER10 0x1d6c -+#define EARCRX_DMAC_CHSTATUS_STREAMER11 0x1d70 -+#define EARCRX_DMAC_CHSTATUS_STREAMER12 0x1d74 -+#define EARCRX_DMAC_CHSTATUS_STREAMER13 0x1d78 -+#define EARCRX_DMAC_CHSTATUS_STREAMER14 0x1d7c -+#define EARCRX_DMAC_USRDATA_STREAMER0 0x1d80 -+/* Main Unit Interrupt Registers */ -+#define MAIN_INTVEC_INDEX 0x3000 -+#define MAINUNIT_0_INT_STATUS 0x3010 -+#define MAINUNIT_0_INT_MASK_N 0x3014 -+#define MAINUNIT_0_INT_CLEAR 0x3018 -+#define MAINUNIT_0_INT_FORCE 0x301c -+#define MAINUNIT_1_INT_STATUS 0x3020 -+#define FLT_EXIT_TO_LTSL_IRQ BIT(22) -+#define FLT_EXIT_TO_LTS4_IRQ BIT(21) -+#define FLT_EXIT_TO_LTSP_IRQ BIT(20) -+#define SCDC_NACK_RCVD_IRQ BIT(12) -+#define SCDC_RR_REPLY_STOP_IRQ BIT(11) -+#define SCDC_UPD_FLAGS_CLR_IRQ BIT(10) -+#define SCDC_UPD_FLAGS_CHG_IRQ BIT(9) -+#define SCDC_UPD_FLAGS_RD_IRQ BIT(8) -+#define I2CM_NACK_RCVD_IRQ BIT(2) -+#define I2CM_READ_REQUEST_IRQ BIT(1) -+#define I2CM_OP_DONE_IRQ BIT(0) -+#define MAINUNIT_1_INT_MASK_N 0x3024 -+#define I2CM_NACK_RCVD_MASK_N BIT(2) -+#define I2CM_READ_REQUEST_MASK_N BIT(1) -+#define I2CM_OP_DONE_MASK_N BIT(0) -+#define MAINUNIT_1_INT_CLEAR 0x3028 -+#define I2CM_NACK_RCVD_CLEAR BIT(2) -+#define I2CM_READ_REQUEST_CLEAR BIT(1) -+#define I2CM_OP_DONE_CLEAR BIT(0) -+#define MAINUNIT_1_INT_FORCE 0x302c -+/* AVPUNIT Interrupt Registers */ -+#define AVP_INTVEC_INDEX 0x3800 -+#define AVP_0_INT_STATUS 0x3810 -+#define AVP_0_INT_MASK_N 0x3814 -+#define AVP_0_INT_CLEAR 0x3818 -+#define AVP_0_INT_FORCE 0x381c -+#define AVP_1_INT_STATUS 0x3820 -+#define AVP_1_INT_MASK_N 0x3824 -+#define HDCP14_AUTH_CHG_MASK_N BIT(6) -+#define AVP_1_INT_CLEAR 0x3828 -+#define AVP_1_INT_FORCE 0x382c -+#define AVP_2_INT_STATUS 0x3830 -+#define AVP_2_INT_MASK_N 0x3834 -+#define AVP_2_INT_CLEAR 0x3838 -+#define AVP_2_INT_FORCE 0x383c -+#define AVP_3_INT_STATUS 0x3840 -+#define AVP_3_INT_MASK_N 0x3844 -+#define AVP_3_INT_CLEAR 0x3848 -+#define AVP_3_INT_FORCE 0x384c -+#define AVP_4_INT_STATUS 0x3850 -+#define AVP_4_INT_MASK_N 0x3854 -+#define AVP_4_INT_CLEAR 0x3858 -+#define AVP_4_INT_FORCE 0x385c -+#define AVP_5_INT_STATUS 0x3860 -+#define AVP_5_INT_MASK_N 0x3864 -+#define AVP_5_INT_CLEAR 0x3868 -+#define AVP_5_INT_FORCE 0x386c -+#define AVP_6_INT_STATUS 0x3870 -+#define AVP_6_INT_MASK_N 0x3874 -+#define AVP_6_INT_CLEAR 0x3878 -+#define AVP_6_INT_FORCE 0x387c -+/* CEC Interrupt Registers */ -+#define CEC_INT_STATUS 0x4000 -+#define CEC_INT_MASK_N 0x4004 -+#define CEC_INT_CLEAR 0x4008 -+#define CEC_INT_FORCE 0x400c -+/* eARC RX Interrupt Registers */ -+#define EARCRX_INTVEC_INDEX 0x4800 -+#define EARCRX_0_INT_STATUS 0x4810 -+#define EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ BIT(9) -+#define EARCRX_CMDC_DISCOVERY_DONE_IRQ BIT(8) -+#define EARCRX_0_INT_MASK_N 0x4814 -+#define EARCRX_0_INT_CLEAR 0x4818 -+#define EARCRX_0_INT_FORCE 0x481c -+#define EARCRX_1_INT_STATUS 0x4820 -+#define EARCRX_1_INT_MASK_N 0x4824 -+#define EARCRX_1_INT_CLEAR 0x4828 -+#define EARCRX_1_INT_FORCE 0x482c -+ -+#endif /* __DW_HDMI_QP_H__ */ -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c -index aca5bb0866f8..1b81a4466942 100644 ---- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c -@@ -9,6 +9,8 @@ - #include - #include - #include -+#include -+#include - #include - #include - #include -@@ -19,6 +21,7 @@ - #include - #include - #include -+#include - - #include - -@@ -26,17 +29,19 @@ - #include - - #include --#include --#include - #include - #include - #include -+#include - #include - #include - #include -+#include -+#include - - #include "dw-hdmi-audio.h" - #include "dw-hdmi-cec.h" -+#include "dw-hdmi-hdcp.h" - #include "dw-hdmi.h" - - #define DDC_CI_ADDR 0x37 -@@ -49,6 +54,80 @@ - - #define HDMI14_MAX_TMDSCLK 340000000 - -+static const unsigned int dw_hdmi_cable[] = { -+ EXTCON_DISP_HDMI, -+ EXTCON_NONE, -+}; -+ -+enum hdmi_datamap { -+ RGB444_8B = 0x01, -+ RGB444_10B = 0x03, -+ RGB444_12B = 0x05, -+ RGB444_16B = 0x07, -+ YCbCr444_8B = 0x09, -+ YCbCr444_10B = 0x0B, -+ YCbCr444_12B = 0x0D, -+ YCbCr444_16B = 0x0F, -+ YCbCr422_8B = 0x16, -+ YCbCr422_10B = 0x14, -+ YCbCr422_12B = 0x12, -+}; -+ -+/* -+ * Unless otherwise noted, entries in this table are 100% optimization. -+ * Values can be obtained from hdmi_compute_n() but that function is -+ * slow so we pre-compute values we expect to see. -+ * -+ * All 32k and 48k values are expected to be the same (due to the way -+ * the math works) for any rate that's an exact kHz. -+ */ -+static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = { -+ { .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, }, -+ { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, }, -+ { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, -+ { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, }, -+ { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, }, -+ { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, -+ { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, -+ { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, -+ { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, -+ { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, -+ { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, -+ { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, -+ { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, -+ { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, -+ { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, }, -+ { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, -+ { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, }, -+ { .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, -+ { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, -+ { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, }, -+ { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, -+ { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, -+ { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, -+ { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, -+ { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, -+ { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, -+ { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, -+ { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, -+ { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, -+ { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, -+ { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, }, -+ { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, -+ { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, -+ { .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, -+ { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, -+ { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, -+ { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, -+ -+ /* For 297 MHz+ HDMI spec have some other rule for setting N */ -+ { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, }, -+ { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, }, -+ -+ /* End of table */ -+ { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, }, -+}; -+ - static const u16 csc_coeff_default[3][4] = { - { 0x2000, 0x0000, 0x0000, 0x0000 }, - { 0x0000, 0x2000, 0x0000, 0x0000 }, -@@ -85,12 +164,47 @@ static const u16 csc_coeff_rgb_full_to_rgb_limited[3][4] = { - { 0x0000, 0x0000, 0x1b7c, 0x0020 } - }; - -+static const struct drm_display_mode dw_hdmi_default_modes[] = { -+ /* 4 - 1280x720@60Hz 16:9 */ -+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, -+ 1430, 1650, 0, 720, 725, 730, 750, 0, -+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, -+ /* 16 - 1920x1080@60Hz 16:9 */ -+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, -+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, -+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, -+ /* 31 - 1920x1080@50Hz 16:9 */ -+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, -+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, -+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, -+ /* 19 - 1280x720@50Hz 16:9 */ -+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, -+ 1760, 1980, 0, 720, 725, 730, 750, 0, -+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, -+ /* 17 - 720x576@50Hz 4:3 */ -+ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, -+ 796, 864, 0, 576, 581, 586, 625, 0, -+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, -+ /* 2 - 720x480@60Hz 4:3 */ -+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, -+ 798, 858, 0, 480, 489, 495, 525, 0, -+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, -+}; -+ - struct hdmi_vmode { - bool mdataenablepolarity; - -+ unsigned int previous_pixelclock; - unsigned int mpixelclock; - unsigned int mpixelrepetitioninput; - unsigned int mpixelrepetitionoutput; -+ unsigned int previous_tmdsclock; - unsigned int mtmdsclock; - }; - -@@ -99,8 +213,8 @@ struct hdmi_data_info { - unsigned int enc_out_bus_format; - unsigned int enc_in_encoding; - unsigned int enc_out_encoding; -+ unsigned int quant_range; - unsigned int pix_repet_factor; -- unsigned int hdcp_enable; - struct hdmi_vmode video_mode; - bool rgb_limited_range; - }; -@@ -115,6 +229,9 @@ struct dw_hdmi_i2c { - u8 slave_reg; - bool is_regaddr; - bool is_segment; -+ -+ unsigned int scl_high_ns; -+ unsigned int scl_low_ns; - }; - - struct dw_hdmi_phy_data { -@@ -131,6 +248,7 @@ struct dw_hdmi { - struct drm_connector connector; - struct drm_bridge bridge; - struct drm_bridge *next_bridge; -+ struct platform_device *hdcp_dev; - - unsigned int version; - -@@ -144,8 +262,10 @@ struct dw_hdmi { - - struct hdmi_data_info hdmi_data; - const struct dw_hdmi_plat_data *plat_data; -+ struct dw_hdcp *hdcp; - - int vic; -+ int irq; - - u8 edid[HDMI_EDID_LEN]; - -@@ -162,6 +282,13 @@ struct dw_hdmi { - void __iomem *regs; - bool sink_is_hdmi; - bool sink_has_audio; -+ bool hpd_state; -+ bool support_hdmi; -+ bool force_logo; -+ int force_output; -+ -+ struct delayed_work work; -+ struct workqueue_struct *workqueue; - - struct pinctrl *pinctrl; - struct pinctrl_state *default_state; -@@ -178,13 +305,14 @@ struct dw_hdmi { - - spinlock_t audio_lock; - struct mutex audio_mutex; -- unsigned int sample_non_pcm; -- unsigned int sample_width; -+ struct dentry *debugfs_dir; - unsigned int sample_rate; -- unsigned int channels; - unsigned int audio_cts; - unsigned int audio_n; - bool audio_enable; -+ bool scramble_low_rates; -+ -+ struct extcon_dev *extcon; - - unsigned int reg_shift; - struct regmap *regm; -@@ -193,10 +321,12 @@ struct dw_hdmi { - - struct mutex cec_notifier_mutex; - struct cec_notifier *cec_notifier; -+ struct cec_adapter *cec_adap; - - hdmi_codec_plugged_cb plugged_cb; - struct device *codec_dev; - enum drm_connector_status last_connector_result; -+ bool initialized; /* hdmi is enabled before bind */ - }; - - #define HDMI_IH_PHY_STAT0_RX_SENSE \ -@@ -254,6 +384,124 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, - hdmi_modb(hdmi, data << shift, mask, reg); - } - -+static bool dw_hdmi_check_output_type_changed(struct dw_hdmi *hdmi) -+{ -+ bool sink_hdmi; -+ -+ sink_hdmi = hdmi->sink_is_hdmi; -+ -+ if (hdmi->force_output == 1) -+ hdmi->sink_is_hdmi = true; -+ else if (hdmi->force_output == 2) -+ hdmi->sink_is_hdmi = false; -+ else -+ hdmi->sink_is_hdmi = hdmi->support_hdmi; -+ -+ if (sink_hdmi != hdmi->sink_is_hdmi) -+ return true; -+ -+ return false; -+} -+ -+static void repo_hpd_event(struct work_struct *p_work) -+{ -+ struct dw_hdmi *hdmi = container_of(p_work, struct dw_hdmi, work.work); -+ enum drm_connector_status status = hdmi->hpd_state ? -+ connector_status_connected : connector_status_disconnected; -+ u8 phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0); -+ -+ mutex_lock(&hdmi->mutex); -+ if (!(phy_stat & HDMI_PHY_RX_SENSE)) -+ hdmi->rxsense = false; -+ if (phy_stat & HDMI_PHY_HPD) -+ hdmi->rxsense = true; -+ mutex_unlock(&hdmi->mutex); -+ -+ if (hdmi->bridge.dev) { -+ bool change; -+ -+ change = drm_helper_hpd_irq_event(hdmi->bridge.dev); -+ if (change && hdmi->cec_adap && -+ hdmi->cec_adap->devnode.registered) -+ cec_queue_pin_hpd_event(hdmi->cec_adap, -+ hdmi->hpd_state, -+ ktime_get()); -+ drm_bridge_hpd_notify(&hdmi->bridge, status); -+ } -+} -+ -+static bool check_hdmi_irq(struct dw_hdmi *hdmi, int intr_stat, -+ int phy_int_pol) -+{ -+ int msecs; -+ -+ /* To determine whether interrupt type is HPD */ -+ if (!(intr_stat & HDMI_IH_PHY_STAT0_HPD)) -+ return false; -+ -+ if (phy_int_pol & HDMI_PHY_HPD) { -+ dev_dbg(hdmi->dev, "dw hdmi plug in\n"); -+ msecs = 150; -+ hdmi->hpd_state = true; -+ } else { -+ dev_dbg(hdmi->dev, "dw hdmi plug out\n"); -+ msecs = 20; -+ hdmi->hpd_state = false; -+ } -+ mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs)); -+ -+ return true; -+} -+ -+static void init_hpd_work(struct dw_hdmi *hdmi) -+{ -+ hdmi->workqueue = create_workqueue("hpd_queue"); -+ INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event); -+} -+ -+static void dw_hdmi_i2c_set_divs(struct dw_hdmi *hdmi) -+{ -+ unsigned long clk_rate_khz; -+ unsigned long low_ns, high_ns; -+ unsigned long div_low, div_high; -+ -+ /* Standard-mode */ -+ if (hdmi->i2c->scl_high_ns < 4000) -+ high_ns = 4708; -+ else -+ high_ns = hdmi->i2c->scl_high_ns; -+ -+ if (hdmi->i2c->scl_low_ns < 4700) -+ low_ns = 4916; -+ else -+ low_ns = hdmi->i2c->scl_low_ns; -+ -+ /* Adjust to avoid overflow */ -+ clk_rate_khz = DIV_ROUND_UP(clk_get_rate(hdmi->isfr_clk), 1000); -+ -+ div_low = (clk_rate_khz * low_ns) / 1000000; -+ if ((clk_rate_khz * low_ns) % 1000000) -+ div_low++; -+ -+ div_high = (clk_rate_khz * high_ns) / 1000000; -+ if ((clk_rate_khz * high_ns) % 1000000) -+ div_high++; -+ -+ /* Maximum divider supported by hw is 0xffff */ -+ if (div_low > 0xffff) -+ div_low = 0xffff; -+ -+ if (div_high > 0xffff) -+ div_high = 0xffff; -+ -+ hdmi_writeb(hdmi, div_high & 0xff, HDMI_I2CM_SS_SCL_HCNT_0_ADDR); -+ hdmi_writeb(hdmi, (div_high >> 8) & 0xff, -+ HDMI_I2CM_SS_SCL_HCNT_1_ADDR); -+ hdmi_writeb(hdmi, div_low & 0xff, HDMI_I2CM_SS_SCL_LCNT_0_ADDR); -+ hdmi_writeb(hdmi, (div_low >> 8) & 0xff, -+ HDMI_I2CM_SS_SCL_LCNT_1_ADDR); -+} -+ - static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) - { - hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, -@@ -267,7 +515,8 @@ static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) - hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ); - - /* Set Standard Mode speed (determined to be 100KHz on iMX6) */ -- hdmi_writeb(hdmi, 0x00, HDMI_I2CM_DIV); -+ hdmi_modb(hdmi, HDMI_I2CM_DIV_STD_MODE, -+ HDMI_I2CM_DIV_FAST_STD_MODE, HDMI_I2CM_DIV); - - /* Set done, not acknowledged and arbitration interrupt polarities */ - hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT); -@@ -281,6 +530,11 @@ static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) - /* Mute DONE and ERROR interrupts */ - hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE, - HDMI_IH_MUTE_I2CM_STAT0); -+ -+ /* set SDA high level holding time */ -+ hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD); -+ -+ dw_hdmi_i2c_set_divs(hdmi); - } - - static bool dw_hdmi_i2c_unwedge(struct dw_hdmi *hdmi) -@@ -452,6 +706,8 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, - hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0); - - /* Set slave device address taken from the first I2C message */ -+ if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1) -+ addr = DDC_ADDR; - hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE); - - /* Set slave device register address on transfer */ -@@ -560,66 +816,117 @@ static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts, - hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1); - } - --static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk) -+static int hdmi_match_tmds_n_table(struct dw_hdmi *hdmi, -+ unsigned long pixel_clk, -+ unsigned long freq) - { -- unsigned int n = (128 * freq) / 1000; -- unsigned int mult = 1; -+ const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data; -+ const struct dw_hdmi_audio_tmds_n *tmds_n = NULL; -+ int i; -+ -+ if (plat_data->tmds_n_table) { -+ for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) { -+ if (pixel_clk == plat_data->tmds_n_table[i].tmds) { -+ tmds_n = &plat_data->tmds_n_table[i]; -+ break; -+ } -+ } -+ } - -- while (freq > 48000) { -- mult *= 2; -- freq /= 2; -+ if (tmds_n == NULL) { -+ for (i = 0; common_tmds_n_table[i].tmds != 0; i++) { -+ if (pixel_clk == common_tmds_n_table[i].tmds) { -+ tmds_n = &common_tmds_n_table[i]; -+ break; -+ } -+ } - } - -+ if (tmds_n == NULL) -+ return -ENOENT; -+ - switch (freq) { - case 32000: -- if (pixel_clk == 25175000) -- n = 4576; -- else if (pixel_clk == 27027000) -- n = 4096; -- else if (pixel_clk == 74176000 || pixel_clk == 148352000) -- n = 11648; -- else if (pixel_clk == 297000000) -- n = 3072; -- else -- n = 4096; -- n *= mult; -- break; -- -+ return tmds_n->n_32k; - case 44100: -- if (pixel_clk == 25175000) -- n = 7007; -- else if (pixel_clk == 74176000) -- n = 17836; -- else if (pixel_clk == 148352000) -- n = 8918; -- else if (pixel_clk == 297000000) -- n = 4704; -- else -- n = 6272; -- n *= mult; -- break; -- -+ case 88200: -+ case 176400: -+ return (freq / 44100) * tmds_n->n_44k1; - case 48000: -- if (pixel_clk == 25175000) -- n = 6864; -- else if (pixel_clk == 27027000) -- n = 6144; -- else if (pixel_clk == 74176000) -- n = 11648; -- else if (pixel_clk == 148352000) -- n = 5824; -- else if (pixel_clk == 297000000) -- n = 5120; -- else -- n = 6144; -- n *= mult; -- break; -- -+ case 96000: -+ case 192000: -+ return (freq / 48000) * tmds_n->n_48k; - default: -- break; -+ return -ENOENT; -+ } -+} -+ -+static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n, -+ unsigned int pixel_clk) -+{ -+ u64 final, diff; -+ u64 cts; -+ -+ final = (u64)pixel_clk * n; -+ -+ cts = final; -+ do_div(cts, 128 * freq); -+ -+ diff = final - (u64)cts * (128 * freq); -+ -+ return diff; -+} -+ -+static unsigned int hdmi_compute_n(struct dw_hdmi *hdmi, -+ unsigned long pixel_clk, -+ unsigned long freq) -+{ -+ unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500); -+ unsigned int max_n = (128 * freq) / 300; -+ unsigned int ideal_n = (128 * freq) / 1000; -+ unsigned int best_n_distance = ideal_n; -+ unsigned int best_n = 0; -+ u64 best_diff = U64_MAX; -+ int n; -+ -+ /* If the ideal N could satisfy the audio math, then just take it */ -+ if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0) -+ return ideal_n; -+ -+ for (n = min_n; n <= max_n; n++) { -+ u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk); -+ -+ if (diff < best_diff || (diff == best_diff && -+ abs(n - ideal_n) < best_n_distance)) { -+ best_n = n; -+ best_diff = diff; -+ best_n_distance = abs(best_n - ideal_n); -+ } -+ -+ /* -+ * The best N already satisfy the audio math, and also be -+ * the closest value to ideal N, so just cut the loop. -+ */ -+ if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance)) -+ break; - } - -- return n; -+ return best_n; -+} -+ -+static unsigned int hdmi_find_n(struct dw_hdmi *hdmi, unsigned long pixel_clk, -+ unsigned long sample_rate) -+{ -+ int n; -+ -+ n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate); -+ if (n > 0) -+ return n; -+ -+ dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n", -+ pixel_clk); -+ -+ return hdmi_compute_n(hdmi, pixel_clk, sample_rate); - } - - /* -@@ -650,12 +957,12 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, - u8 config3; - u64 tmp; - -- n = hdmi_compute_n(sample_rate, pixel_clk); -+ n = hdmi_find_n(hdmi, pixel_clk, sample_rate); - - config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID); - -- /* Compute CTS when using internal AHB audio or General Parallel audio*/ -- if ((config3 & HDMI_CONFIG3_AHBAUDDMA) || (config3 & HDMI_CONFIG3_GPAUD)) { -+ /* Only compute CTS when using internal AHB audio */ -+ if (config3 & HDMI_CONFIG3_AHBAUDDMA) { - /* - * Compute the CTS value from the N value. Note that CTS and N - * can be up to 20 bits in total, so we need 64-bit math. Also -@@ -697,22 +1004,6 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi) - mutex_unlock(&hdmi->audio_mutex); - } - --void dw_hdmi_set_sample_width(struct dw_hdmi *hdmi, unsigned int width) --{ -- mutex_lock(&hdmi->audio_mutex); -- hdmi->sample_width = width; -- mutex_unlock(&hdmi->audio_mutex); --} --EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_width); -- --void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm) --{ -- mutex_lock(&hdmi->audio_mutex); -- hdmi->sample_non_pcm = non_pcm; -- mutex_unlock(&hdmi->audio_mutex); --} --EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_non_pcm); -- - void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate) - { - mutex_lock(&hdmi->audio_mutex); -@@ -728,7 +1019,6 @@ void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt) - u8 layout; - - mutex_lock(&hdmi->audio_mutex); -- hdmi->channels = cnt; - - /* - * For >2 channel PCM audio, we need to select layout 1 -@@ -769,97 +1059,6 @@ static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable) - hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); - } - --static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi) --{ -- if (!hdmi->curr_conn) -- return NULL; -- -- return hdmi->curr_conn->eld; --} -- --static void dw_hdmi_gp_audio_enable(struct dw_hdmi *hdmi) --{ -- const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; -- int sample_freq = 0x2, org_sample_freq = 0xD; -- int ch_mask = BIT(hdmi->channels) - 1; -- -- switch (hdmi->sample_rate) { -- case 32000: -- sample_freq = 0x03; -- org_sample_freq = 0x0C; -- break; -- case 44100: -- sample_freq = 0x00; -- org_sample_freq = 0x0F; -- break; -- case 48000: -- sample_freq = 0x02; -- org_sample_freq = 0x0D; -- break; -- case 88200: -- sample_freq = 0x08; -- org_sample_freq = 0x07; -- break; -- case 96000: -- sample_freq = 0x0A; -- org_sample_freq = 0x05; -- break; -- case 176400: -- sample_freq = 0x0C; -- org_sample_freq = 0x03; -- break; -- case 192000: -- sample_freq = 0x0E; -- org_sample_freq = 0x01; -- break; -- default: -- break; -- } -- -- hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); -- hdmi_enable_audio_clk(hdmi, true); -- -- hdmi_writeb(hdmi, 0x1, HDMI_FC_AUDSCHNLS0); -- hdmi_writeb(hdmi, hdmi->channels, HDMI_FC_AUDSCHNLS2); -- hdmi_writeb(hdmi, 0x22, HDMI_FC_AUDSCHNLS3); -- hdmi_writeb(hdmi, 0x22, HDMI_FC_AUDSCHNLS4); -- hdmi_writeb(hdmi, 0x11, HDMI_FC_AUDSCHNLS5); -- hdmi_writeb(hdmi, 0x11, HDMI_FC_AUDSCHNLS6); -- hdmi_writeb(hdmi, (0x3 << 4) | sample_freq, HDMI_FC_AUDSCHNLS7); -- hdmi_writeb(hdmi, (org_sample_freq << 4) | 0xb, HDMI_FC_AUDSCHNLS8); -- -- hdmi_writeb(hdmi, ch_mask, HDMI_GP_CONF1); -- hdmi_writeb(hdmi, 0x02, HDMI_GP_CONF2); -- hdmi_writeb(hdmi, 0x01, HDMI_GP_CONF0); -- -- hdmi_modb(hdmi, 0x3, 0x3, HDMI_FC_DATAUTO3); -- -- /* hbr */ -- if (hdmi->sample_rate == 192000 && hdmi->channels == 8 && -- hdmi->sample_width == 32 && hdmi->sample_non_pcm) -- hdmi_modb(hdmi, 0x01, 0x01, HDMI_GP_CONF2); -- -- if (pdata->enable_audio) -- pdata->enable_audio(hdmi, -- hdmi->channels, -- hdmi->sample_width, -- hdmi->sample_rate, -- hdmi->sample_non_pcm); --} -- --static void dw_hdmi_gp_audio_disable(struct dw_hdmi *hdmi) --{ -- const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; -- -- hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0); -- -- hdmi_modb(hdmi, 0, 0x3, HDMI_FC_DATAUTO3); -- if (pdata->disable_audio) -- pdata->disable_audio(hdmi); -- -- hdmi_enable_audio_clk(hdmi, false); --} -- - static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi) - { - hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); -@@ -1109,6 +1308,15 @@ static bool is_csc_needed(struct dw_hdmi *hdmi) - is_color_space_interpolation(hdmi); - } - -+static bool is_rgb_full_to_limited_needed(struct dw_hdmi *hdmi) -+{ -+ if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED || -+ (!hdmi->hdmi_data.quant_range && hdmi->hdmi_data.rgb_limited_range)) -+ return true; -+ -+ return false; -+} -+ - static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi) - { - const u16 (*csc_coeff)[3][4] = &csc_coeff_default; -@@ -1131,7 +1339,7 @@ static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi) - csc_coeff = &csc_coeff_rgb_in_eitu709; - csc_scale = 0; - } else if (is_input_rgb && is_output_rgb && -- hdmi->hdmi_data.rgb_limited_range) { -+ is_rgb_full_to_limited_needed(hdmi)) { - csc_coeff = &csc_coeff_rgb_full_to_rgb_limited; - } - -@@ -1163,7 +1371,7 @@ static void hdmi_video_csc(struct dw_hdmi *hdmi) - if (is_color_space_interpolation(hdmi)) - interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1; - else if (is_color_space_decimation(hdmi)) -- decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3; -+ decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1; - - switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) { - case 8: -@@ -1203,8 +1411,6 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) - unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP; - struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; - u8 val, vp_conf; -- u8 clear_gcp_auto = 0; -- - - if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || - hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) || -@@ -1212,9 +1418,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) - switch (hdmi_bus_fmt_color_depth( - hdmi->hdmi_data.enc_out_bus_format)) { - case 8: -- color_depth = 4; -+ color_depth = 0; - output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; -- clear_gcp_auto = 1; - break; - case 10: - color_depth = 5; -@@ -1234,7 +1439,6 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) - case 0: - case 8: - remap_size = HDMI_VP_REMAP_YCC422_16bit; -- clear_gcp_auto = 1; - break; - case 10: - remap_size = HDMI_VP_REMAP_YCC422_20bit; -@@ -1252,31 +1456,15 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) - } - - /* set the packetizer registers */ -- val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) & -- HDMI_VP_PR_CD_COLOR_DEPTH_MASK) | -- ((hdmi_data->pix_repet_factor << -- HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) & -- HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK); -+ val = (color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) & -+ HDMI_VP_PR_CD_COLOR_DEPTH_MASK; - hdmi_writeb(hdmi, val, HDMI_VP_PR_CD); - -- /* HDMI1.4b specification section 6.5.3: -- * Source shall only send GCPs with non-zero CD to sinks -- * that indicate support for Deep Color. -- * GCP only transmit CD and do not handle AVMUTE, PP norDefault_Phase (yet). -- * Disable Auto GCP when 24-bit color for sinks that not support Deep Color. -- */ -- val = hdmi_readb(hdmi, HDMI_FC_DATAUTO3); -- if (clear_gcp_auto == 1) -- val &= ~HDMI_FC_DATAUTO3_GCP_AUTO; -- else -- val |= HDMI_FC_DATAUTO3_GCP_AUTO; -- hdmi_writeb(hdmi, val, HDMI_FC_DATAUTO3); -- - hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE, - HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF); - - /* Data from pixel repeater block */ -- if (hdmi_data->pix_repet_factor > 1) { -+ if (hdmi_data->pix_repet_factor > 0) { - vp_conf = HDMI_VP_CONF_PR_EN_ENABLE | - HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER; - } else { /* data from packetizer block */ -@@ -1288,8 +1476,13 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) - HDMI_VP_CONF_PR_EN_MASK | - HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); - -- hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET, -- HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); -+ if ((color_depth == 5 && hdmi->previous_mode.htotal % 4) || -+ (color_depth == 6 && hdmi->previous_mode.htotal % 2)) -+ hdmi_modb(hdmi, 0, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, -+ HDMI_VP_STUFF); -+ else -+ hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET, -+ HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); - - hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP); - -@@ -1390,6 +1583,23 @@ static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi, - return true; - } - -+static int hdmi_phy_i2c_read(struct dw_hdmi *hdmi, unsigned char addr) -+{ -+ int val; -+ -+ hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); -+ hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR); -+ hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_1_ADDR); -+ hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_0_ADDR); -+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_READ, -+ HDMI_PHY_I2CM_OPERATION_ADDR); -+ hdmi_phy_wait_i2c_done(hdmi, 1000); -+ val = hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_1_ADDR); -+ val = (val & 0xff) << 8; -+ val += hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_0_ADDR) & 0xff; -+ return val; -+} -+ - /* - * HDMI2.0 Specifies the following procedure for High TMDS Bit Rates: - * - The Source shall suspend transmission of the TMDS clock and data -@@ -1575,6 +1785,13 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi, - const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg; - const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; - const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; -+ unsigned int tmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock; -+ unsigned int depth = -+ hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); -+ -+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) && -+ pdata->mpll_cfg_420) -+ mpll_config = pdata->mpll_cfg_420; - - /* TOFIX Will need 420 specific PHY configuration tables */ - -@@ -1584,11 +1801,11 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi, - break; - - for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++) -- if (mpixelclock <= curr_ctrl->mpixelclock) -+ if (tmdsclock <= curr_ctrl->mpixelclock) - break; - - for (; phy_config->mpixelclock != ~0UL; phy_config++) -- if (mpixelclock <= phy_config->mpixelclock) -+ if (tmdsclock <= phy_config->mpixelclock) - break; - - if (mpll_config->mpixelclock == ~0UL || -@@ -1596,11 +1813,18 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi, - phy_config->mpixelclock == ~0UL) - return -EINVAL; - -- dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce, -+ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) -+ depth = fls(depth - 8); -+ else -+ depth = 0; -+ if (depth) -+ depth--; -+ -+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce, - HDMI_3D_TX_PHY_CPCE_CTRL); -- dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, -+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp, - HDMI_3D_TX_PHY_GMPCTRL); -- dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], -+ dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth], - HDMI_3D_TX_PHY_CURRCTRL); - - dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL); -@@ -1613,10 +1837,6 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi, - dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, - HDMI_3D_TX_PHY_VLEVCTRL); - -- /* Override and disable clock termination. */ -- dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE, -- HDMI_3D_TX_PHY_CKCALCTRL); -- - return 0; - } - -@@ -1718,14 +1938,16 @@ void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data) - hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, - HDMI_IH_PHY_STAT0); - -- /* Enable cable hot plug irq. */ -- hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); -+ if (!hdmi->next_bridge) { -+ /* Enable cable hot plug irq. */ -+ hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); - -- /* Clear and unmute interrupts. */ -- hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, -- HDMI_IH_PHY_STAT0); -- hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), -- HDMI_IH_MUTE_PHY_STAT0); -+ /* Clear and unmute interrupts. */ -+ hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, -+ HDMI_IH_PHY_STAT0); -+ hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), -+ HDMI_IH_MUTE_PHY_STAT0); -+ } - } - EXPORT_SYMBOL_GPL(dw_hdmi_phy_setup_hpd); - -@@ -1741,23 +1963,36 @@ static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = { - * HDMI TX Setup - */ - --static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi) -+static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi, -+ const struct drm_display_mode *mode) - { -- u8 de; -- -- if (hdmi->hdmi_data.video_mode.mdataenablepolarity) -- de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH; -- else -- de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW; -- -- /* disable rx detect */ -- hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE, -- HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0); -- -- hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG); -- -- hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE, -- HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1); -+ struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; -+ u8 vsync_pol, hsync_pol, data_pol, hdmi_dvi; -+ -+ /* Configure the video polarity */ -+ vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ? -+ HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH : -+ HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW; -+ hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ? -+ HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH : -+ HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW; -+ data_pol = vmode->mdataenablepolarity ? -+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH : -+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW; -+ hdmi_modb(hdmi, vsync_pol | hsync_pol | data_pol, -+ HDMI_A_VIDPOLCFG_VSYNCPOL_MASK | -+ HDMI_A_VIDPOLCFG_HSYNCPOL_MASK | -+ HDMI_A_VIDPOLCFG_DATAENPOL_MASK, -+ HDMI_A_VIDPOLCFG); -+ -+ /* Config the display mode */ -+ hdmi_dvi = hdmi->sink_is_hdmi ? HDMI_A_HDCPCFG0_HDMIDVI_HDMI : -+ HDMI_A_HDCPCFG0_HDMIDVI_DVI; -+ hdmi_modb(hdmi, hdmi_dvi, HDMI_A_HDCPCFG0_HDMIDVI_MASK, -+ HDMI_A_HDCPCFG0); -+ -+ if (hdmi->hdcp && hdmi->hdcp->hdcp_start) -+ hdmi->hdcp->hdcp_start(hdmi->hdcp); - } - - static void hdmi_config_AVI(struct dw_hdmi *hdmi, -@@ -1771,10 +2006,15 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, - drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode); - - if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { -- drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, -- hdmi->hdmi_data.rgb_limited_range ? -- HDMI_QUANTIZATION_RANGE_LIMITED : -- HDMI_QUANTIZATION_RANGE_FULL); -+ /* default range */ -+ if (!hdmi->hdmi_data.quant_range) -+ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, -+ hdmi->hdmi_data.rgb_limited_range ? -+ HDMI_QUANTIZATION_RANGE_LIMITED : -+ HDMI_QUANTIZATION_RANGE_FULL); -+ else -+ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, -+ hdmi->hdmi_data.quant_range); - } else { - frame.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; - frame.ycc_quantization_range = -@@ -1809,6 +2049,14 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, - frame.extended_colorimetry = - HDMI_EXTENDED_COLORIMETRY_XV_YCC_709; - break; -+ case V4L2_YCBCR_ENC_BT2020: -+ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_BT2020) -+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; -+ else -+ frame.colorimetry = HDMI_COLORIMETRY_ITU_709; -+ frame.extended_colorimetry = -+ HDMI_EXTENDED_COLORIMETRY_BT2020; -+ break; - default: /* Carries no data */ - frame.colorimetry = HDMI_COLORIMETRY_ITU_601; - frame.extended_colorimetry = -@@ -1945,17 +2193,44 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi, - const struct drm_connector *connector) - { - const struct drm_connector_state *conn_state = connector->state; -+ struct hdr_output_metadata *hdr_metadata; - struct hdmi_drm_infoframe frame; - u8 buffer[30]; - ssize_t err; - int i; - -+ /* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */ -+ if (hdmi->version < 0x211a) { -+ DRM_ERROR("Not support DRM Infoframe\n"); -+ return; -+ } -+ - if (!hdmi->plat_data->use_drm_infoframe) - return; - - hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_DISABLE, - HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN); - -+ if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) { -+ DRM_DEBUG("No need to set HDR metadata in infoframe\n"); -+ return; -+ } -+ -+ if (!conn_state->hdr_output_metadata) { -+ DRM_DEBUG("source metadata not set yet\n"); -+ return; -+ } -+ -+ hdr_metadata = (struct hdr_output_metadata *) -+ conn_state->hdr_output_metadata->data; -+ -+ if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf & -+ BIT(hdr_metadata->hdmi_metadata_type1.eotf))) { -+ DRM_ERROR("Not support EOTF %d\n", -+ hdr_metadata->hdmi_metadata_type1.eotf); -+ return; -+ } -+ - err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); - if (err < 0) - return; -@@ -1975,51 +2250,66 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi, - hdmi_writeb(hdmi, 1, HDMI_FC_DRM_UP); - hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_ENABLE, - HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN); -+ -+ DRM_DEBUG("%s eotf %d end\n", __func__, -+ hdr_metadata->hdmi_metadata_type1.eotf); - } - --static void hdmi_av_composer(struct dw_hdmi *hdmi, -- const struct drm_display_info *display, -- const struct drm_display_mode *mode) -+static unsigned int -+hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock) - { -- u8 inv_val, bytes; -- const struct drm_hdmi_info *hdmi_info = &display->hdmi; -- struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; -- int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; -- unsigned int vdisplay, hdisplay; -- -- vmode->mpixelclock = mode->clock * 1000; -- -- dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); -- -- vmode->mtmdsclock = vmode->mpixelclock; -+ unsigned int tmdsclock = mpixelclock; -+ unsigned int depth = -+ hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); - - if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { -- switch (hdmi_bus_fmt_color_depth( -- hdmi->hdmi_data.enc_out_bus_format)) { -+ switch (depth) { - case 16: -- vmode->mtmdsclock = vmode->mpixelclock * 2; -+ tmdsclock = mpixelclock * 2; - break; - case 12: -- vmode->mtmdsclock = vmode->mpixelclock * 3 / 2; -+ tmdsclock = mpixelclock * 3 / 2; - break; - case 10: -- vmode->mtmdsclock = vmode->mpixelclock * 5 / 4; -+ tmdsclock = mpixelclock * 5 / 4; -+ break; -+ default: - break; - } - } - -+ return tmdsclock; -+} -+ -+static void hdmi_av_composer(struct dw_hdmi *hdmi, -+ const struct drm_display_info *display, -+ const struct drm_display_mode *mode) -+{ -+ u8 inv_val, bytes; -+ const struct drm_hdmi_info *hdmi_info = &display->hdmi; -+ struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; -+ int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; -+ unsigned int vdisplay, hdisplay; -+ -+ vmode->previous_pixelclock = vmode->mpixelclock; -+ vmode->mpixelclock = mode->crtc_clock * 1000; -+ if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == -+ DRM_MODE_FLAG_3D_FRAME_PACKING) -+ vmode->mpixelclock *= 2; -+ dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); -+ -+ vmode->previous_tmdsclock = vmode->mtmdsclock; -+ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); - if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) - vmode->mtmdsclock /= 2; -- - dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock); - -- /* Set up HDMI_FC_INVIDCONF */ -- inv_val = (hdmi->hdmi_data.hdcp_enable || -- (dw_hdmi_support_scdc(hdmi, display) && -- (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK || -- hdmi_info->scdc.scrambling.low_rates)) ? -- HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : -- HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); -+ /* Set up HDMI_FC_INVIDCONF -+ * Some display equipments require that the interval -+ * between Video Data and Data island must be at least 58 pixels, -+ * and fc_invidconf.HDCP_keepout set (1'b1) can meet the requirement. -+ */ -+ inv_val = HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE; - - inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ? - HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH : -@@ -2085,7 +2375,8 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, - /* Scrambling Control */ - if (dw_hdmi_support_scdc(hdmi, display)) { - if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK || -- hdmi_info->scdc.scrambling.low_rates) { -+ (hdmi_info->scdc.scrambling.low_rates && -+ hdmi->scramble_low_rates)) { - /* - * HDMI2.0 Specifies the following procedure: - * After the Source Device has determined that -@@ -2119,6 +2410,8 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, - HDMI_MC_SWRSTZ); - drm_scdc_set_scrambling(hdmi->curr_conn, 0); - } -+ } else { -+ hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL); - } - - /* Set up horizontal active pixel width */ -@@ -2176,6 +2469,12 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) - hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; - hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); - -+ /* Enable pixel repetition path */ -+ if (hdmi->hdmi_data.video_mode.mpixelrepetitioninput) { -+ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE; -+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); -+ } -+ - /* Enable csc path */ - if (is_csc_needed(hdmi)) { - hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; -@@ -2206,22 +2505,31 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) - * then write one of the FC registers several times. - * - * The number of iterations matters and depends on the HDMI TX revision -- * (and possibly on the platform). -- * 4 iterations for i.MX6Q(v1.30a) and 1 iteration for others. -- * i.MX6DL (v1.31a), Allwinner SoCs (v1.32a), Rockchip RK3288 SoC (v2.00a), -- * Amlogic Meson GX SoCs (v2.01a), RK3328/RK3399 SoCs (v2.11a) -- * and i.MX8MPlus (v2.13a) have been identified as needing the workaround -- * with a single iteration. -+ * (and possibly on the platform). So far i.MX6Q (v1.30a), i.MX6DL -+ * (v1.31a) and multiple Allwinner SoCs (v1.32a) have been identified -+ * as needing the workaround, with 4 iterations for v1.30a and 1 -+ * iteration for others. -+ * The Amlogic Meson GX SoCs (v2.01a) have been identified as needing -+ * the workaround with a single iteration. -+ * The Rockchip RK3288 SoC (v2.00a) and RK3328/RK3399 SoCs (v2.11a) have -+ * been identified as needing the workaround with a single iteration. - */ - - switch (hdmi->version) { - case 0x130a: - count = 4; - break; -- default: -+ case 0x131a: -+ case 0x132a: -+ case 0x200a: -+ case 0x201a: -+ case 0x211a: -+ case 0x212a: - count = 1; - break; -- } -+ default: -+ return; -+ } - - /* TMDS software reset */ - hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); -@@ -2242,6 +2550,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, - const struct drm_display_mode *mode) - { - int ret; -+ void *data = hdmi->plat_data->phy_data; - - hdmi_disable_overflow_interrupts(hdmi); - -@@ -2253,48 +2562,91 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, - dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic); - } - -- if ((hdmi->vic == 6) || (hdmi->vic == 7) || -- (hdmi->vic == 21) || (hdmi->vic == 22) || -- (hdmi->vic == 2) || (hdmi->vic == 3) || -- (hdmi->vic == 17) || (hdmi->vic == 18)) -+ if (hdmi->plat_data->get_enc_out_encoding) -+ hdmi->hdmi_data.enc_out_encoding = -+ hdmi->plat_data->get_enc_out_encoding(data); -+ else if ((hdmi->vic == 6) || (hdmi->vic == 7) || -+ (hdmi->vic == 21) || (hdmi->vic == 22) || -+ (hdmi->vic == 2) || (hdmi->vic == 3) || -+ (hdmi->vic == 17) || (hdmi->vic == 18)) - hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601; - else - hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709; - -- hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; -- hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; -+ if (mode->flags & DRM_MODE_FLAG_DBLCLK) { -+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1; -+ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1; -+ } else { -+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; -+ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; -+ } -+ /* TOFIX: Get input format from plat data or fallback to RGB888 */ -+ if (hdmi->plat_data->get_input_bus_format) -+ hdmi->hdmi_data.enc_in_bus_format = -+ hdmi->plat_data->get_input_bus_format(data); -+ else if (hdmi->plat_data->input_bus_format) -+ hdmi->hdmi_data.enc_in_bus_format = -+ hdmi->plat_data->input_bus_format; -+ else -+ hdmi->hdmi_data.enc_in_bus_format = -+ MEDIA_BUS_FMT_RGB888_1X24; - -- if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED) -- hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24; -+ /* TOFIX: Default to RGB888 output format */ -+ if (hdmi->plat_data->get_output_bus_format) -+ hdmi->hdmi_data.enc_out_bus_format = -+ hdmi->plat_data->get_output_bus_format(data); -+ else -+ hdmi->hdmi_data.enc_out_bus_format = -+ MEDIA_BUS_FMT_RGB888_1X24; - - /* TOFIX: Get input encoding from plat data or fallback to none */ -- if (hdmi->plat_data->input_bus_encoding) -+ if (hdmi->plat_data->get_enc_in_encoding) -+ hdmi->hdmi_data.enc_in_encoding = -+ hdmi->plat_data->get_enc_in_encoding(data); -+ else if (hdmi->plat_data->input_bus_encoding) - hdmi->hdmi_data.enc_in_encoding = - hdmi->plat_data->input_bus_encoding; - else - hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT; - -- if (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED) -- hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24; -+ -+ if (hdmi->plat_data->get_quant_range) -+ hdmi->hdmi_data.quant_range = -+ hdmi->plat_data->get_quant_range(data); - - hdmi->hdmi_data.rgb_limited_range = hdmi->sink_is_hdmi && - drm_default_rgb_quant_range(mode) == - HDMI_QUANTIZATION_RANGE_LIMITED; - -- hdmi->hdmi_data.pix_repet_factor = 0; -- hdmi->hdmi_data.hdcp_enable = 0; -+ if (!hdmi->sink_is_hdmi) -+ hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_FULL; -+ -+ /* -+ * According to the dw-hdmi specification 6.4.2 -+ * vp_pr_cd[3:0]: -+ * 0000b: No pixel repetition (pixel sent only once) -+ * 0001b: Pixel sent two times (pixel repeated once) -+ */ -+ hdmi->hdmi_data.pix_repet_factor = -+ (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0; - hdmi->hdmi_data.video_mode.mdataenablepolarity = true; - - /* HDMI Initialization Step B.1 */ - hdmi_av_composer(hdmi, &connector->display_info, mode); - - /* HDMI Initializateion Step B.2 */ -- ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, -- &connector->display_info, -- &hdmi->previous_mode); -- if (ret) -- return ret; -- hdmi->phy.enabled = true; -+ if (!hdmi->phy.enabled || -+ hdmi->hdmi_data.video_mode.previous_pixelclock != -+ hdmi->hdmi_data.video_mode.mpixelclock || -+ hdmi->hdmi_data.video_mode.previous_tmdsclock != -+ hdmi->hdmi_data.video_mode.mtmdsclock) { -+ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, -+ &connector->display_info, -+ &hdmi->previous_mode); -+ if (ret) -+ return ret; -+ hdmi->phy.enabled = true; -+ } - - /* HDMI Initialization Step B.3 */ - dw_hdmi_enable_video_path(hdmi); -@@ -2322,7 +2674,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, - hdmi_video_packetize(hdmi); - hdmi_video_csc(hdmi); - hdmi_video_sample(hdmi); -- hdmi_tx_hdcp_config(hdmi); -+ hdmi_tx_hdcp_config(hdmi, mode); - - dw_hdmi_clear_overflow(hdmi); - -@@ -2398,6 +2750,8 @@ static void dw_hdmi_poweroff(struct dw_hdmi *hdmi) - hdmi->phy.enabled = false; - } - -+ if (hdmi->hdcp && hdmi->hdcp->hdcp_stop) -+ hdmi->hdcp->hdcp_stop(hdmi->hdcp); - hdmi->bridge_is_on = false; - } - -@@ -2415,6 +2769,10 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi) - } - - if (force == DRM_FORCE_OFF) { -+ if (hdmi->initialized) { -+ hdmi->initialized = false; -+ hdmi->disabled = true; -+ } - if (hdmi->bridge_is_on) - dw_hdmi_poweroff(hdmi); - } else { -@@ -2447,8 +2805,28 @@ static enum drm_connector_status dw_hdmi_detect(struct dw_hdmi *hdmi) - { - enum drm_connector_status result; - -+ if (!hdmi->force_logo) { -+ mutex_lock(&hdmi->mutex); -+ hdmi->force = DRM_FORCE_UNSPECIFIED; -+ dw_hdmi_update_power(hdmi); -+ dw_hdmi_update_phy_mask(hdmi); -+ mutex_unlock(&hdmi->mutex); -+ } -+ - result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); -- hdmi->last_connector_result = result; -+ mutex_lock(&hdmi->mutex); -+ if (result != hdmi->last_connector_result) { -+ dev_dbg(hdmi->dev, "read_hpd result: %d", result); -+ handle_plugged_change(hdmi, -+ result == connector_status_connected); -+ hdmi->last_connector_result = result; -+ } -+ mutex_unlock(&hdmi->mutex); -+ -+ if (result == connector_status_connected) -+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true); -+ else -+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false); - - return result; - } -@@ -2470,7 +2848,7 @@ static struct edid *dw_hdmi_get_edid(struct dw_hdmi *hdmi, - dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", - edid->width_cm, edid->height_cm); - -- hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); -+ hdmi->support_hdmi = drm_detect_hdmi_monitor(edid); - hdmi->sink_has_audio = drm_detect_monitor_audio(edid); - - return edid; -@@ -2488,25 +2866,123 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force) - return dw_hdmi_detect(hdmi); - } - -+static int -+dw_hdmi_update_hdr_property(struct drm_connector *connector) -+{ -+ struct drm_device *dev = connector->dev; -+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, -+ connector); -+ void *data = hdmi->plat_data->phy_data; -+ const struct hdr_static_metadata *metadata = -+ &connector->hdr_sink_metadata.hdmi_type1; -+ size_t size = sizeof(*metadata); -+ struct drm_property *property; -+ struct drm_property_blob *blob; -+ int ret; -+ -+ if (hdmi->plat_data->get_hdr_property) -+ property = hdmi->plat_data->get_hdr_property(data); -+ else -+ return -EINVAL; -+ -+ if (hdmi->plat_data->get_hdr_blob) -+ blob = hdmi->plat_data->get_hdr_blob(data); -+ else -+ return -EINVAL; -+ -+ ret = drm_property_replace_global_blob(dev, &blob, size, metadata, -+ &connector->base, property); -+ return ret; -+} -+ - static int dw_hdmi_connector_get_modes(struct drm_connector *connector) - { - struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, - connector); -+ struct hdr_static_metadata *metedata = -+ &connector->hdr_sink_metadata.hdmi_type1; - struct edid *edid; -- int ret; -+ struct drm_display_mode *mode; -+ struct drm_display_info *info = &connector->display_info; -+ int i, ret = 0; - -+ memset(metedata, 0, sizeof(*metedata)); - edid = dw_hdmi_get_edid(hdmi, connector); -- if (!edid) -- return 0; -+ if (edid) { -+ dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", -+ edid->width_cm, edid->height_cm); -+ drm_connector_update_edid_property(connector, edid); -+ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); -+ ret = drm_add_edid_modes(connector, edid); -+ dw_hdmi_update_hdr_property(connector); -+ kfree(edid); -+ } else { -+ hdmi->support_hdmi = true; -+ hdmi->sink_has_audio = true; -+ for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) { -+ const struct drm_display_mode *ptr = -+ &dw_hdmi_default_modes[i]; -+ -+ mode = drm_mode_duplicate(connector->dev, ptr); -+ if (mode) { -+ if (!i) { -+ mode->type = DRM_MODE_TYPE_PREFERRED; -+ mode->picture_aspect_ratio = -+ HDMI_PICTURE_ASPECT_NONE; -+ } -+ drm_mode_probed_add(connector, mode); -+ ret++; -+ } -+ } -+ info->edid_hdmi_rgb444_dc_modes = 0; -+ info->edid_hdmi_ycbcr444_dc_modes = 0; -+ info->hdmi.y420_dc_modes = 0; -+ info->color_formats = 0; - -- drm_connector_update_edid_property(connector, edid); -- cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); -- ret = drm_add_edid_modes(connector, edid); -- kfree(edid); -+ dev_info(hdmi->dev, "failed to get edid\n"); -+ } -+ dw_hdmi_check_output_type_changed(hdmi); -+ -+ return ret; -+} -+ -+static struct drm_encoder * -+dw_hdmi_connector_best_encoder(struct drm_connector *connector) -+{ -+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, -+ connector); -+ -+ return hdmi->bridge.encoder; -+} -+ -+static bool dw_hdmi_color_changed(struct drm_connector *connector) -+{ -+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, -+ connector); -+ void *data = hdmi->plat_data->phy_data; -+ bool ret = false; -+ -+ if (hdmi->plat_data->get_color_changed) -+ ret = hdmi->plat_data->get_color_changed(data); - - return ret; - } - -+static bool hdr_metadata_equal(const struct drm_connector_state *old_state, -+ const struct drm_connector_state *new_state) -+{ -+ struct drm_property_blob *old_blob = old_state->hdr_output_metadata; -+ struct drm_property_blob *new_blob = new_state->hdr_output_metadata; -+ -+ if (!old_blob || !new_blob) -+ return old_blob == new_blob; -+ -+ if (old_blob->length != new_blob->length) -+ return false; -+ -+ return !memcmp(old_blob->data, new_blob->data, old_blob->length); -+} -+ - static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, - struct drm_atomic_state *state) - { -@@ -2516,11 +2992,54 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, - drm_atomic_get_new_connector_state(state, connector); - struct drm_crtc *crtc = new_state->crtc; - struct drm_crtc_state *crtc_state; -+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, -+ connector); -+ struct drm_display_mode *mode = NULL; -+ void *data = hdmi->plat_data->phy_data; -+ struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; -+ unsigned int in_bus_format = hdmi->hdmi_data.enc_in_bus_format; -+ unsigned int out_bus_format = hdmi->hdmi_data.enc_out_bus_format; -+ bool color_changed = false; - - if (!crtc) - return 0; - -- if (!drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) { -+ /* -+ * If HDMI is enabled in uboot, it's need to record -+ * drm_display_mode and set phy status to enabled. -+ */ -+ if (!vmode->mpixelclock) { -+ crtc_state = drm_atomic_get_crtc_state(state, crtc); -+ if (hdmi->plat_data->get_enc_in_encoding) -+ hdmi->hdmi_data.enc_in_encoding = -+ hdmi->plat_data->get_enc_in_encoding(data); -+ if (hdmi->plat_data->get_enc_out_encoding) -+ hdmi->hdmi_data.enc_out_encoding = -+ hdmi->plat_data->get_enc_out_encoding(data); -+ if (hdmi->plat_data->get_input_bus_format) -+ hdmi->hdmi_data.enc_in_bus_format = -+ hdmi->plat_data->get_input_bus_format(data); -+ if (hdmi->plat_data->get_output_bus_format) -+ hdmi->hdmi_data.enc_out_bus_format = -+ hdmi->plat_data->get_output_bus_format(data); -+ -+ mode = &crtc_state->mode; -+ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); -+ vmode->mpixelclock = mode->crtc_clock * 1000; -+ vmode->previous_pixelclock = mode->clock; -+ vmode->previous_tmdsclock = mode->clock; -+ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, -+ vmode->mpixelclock); -+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) -+ vmode->mtmdsclock /= 2; -+ -+ if (in_bus_format != hdmi->hdmi_data.enc_in_bus_format || -+ out_bus_format != hdmi->hdmi_data.enc_out_bus_format) -+ color_changed = true; -+ } -+ -+ if (!hdr_metadata_equal(old_state, new_state) || -+ dw_hdmi_color_changed(connector) || color_changed) { - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); -@@ -2531,12 +3050,105 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, - return 0; - } - -+static int -+dw_hdmi_atomic_connector_set_property(struct drm_connector *connector, -+ struct drm_connector_state *state, -+ struct drm_property *property, -+ uint64_t val) -+{ -+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, -+ connector); -+ const struct dw_hdmi_property_ops *ops = -+ hdmi->plat_data->property_ops; -+ -+ if (ops && ops->set_property) -+ return ops->set_property(connector, state, property, -+ val, hdmi->plat_data->phy_data); -+ else -+ return -EINVAL; -+} -+ -+static int -+dw_hdmi_atomic_connector_get_property(struct drm_connector *connector, -+ const struct drm_connector_state *state, -+ struct drm_property *property, -+ uint64_t *val) -+{ -+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, -+ connector); -+ const struct dw_hdmi_property_ops *ops = -+ hdmi->plat_data->property_ops; -+ -+ if (ops && ops->get_property) -+ return ops->get_property(connector, state, property, -+ val, hdmi->plat_data->phy_data); -+ else -+ return -EINVAL; -+} -+ -+static int -+dw_hdmi_connector_set_property(struct drm_connector *connector, -+ struct drm_property *property, uint64_t val) -+{ -+ return dw_hdmi_atomic_connector_set_property(connector, NULL, -+ property, val); -+} -+ -+void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi) -+{ -+ if (!hdmi->bridge_is_on) -+ return; -+ -+ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); -+ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); -+ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_set_quant_range); -+ -+void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val) -+{ -+ hdmi->force_output = val; -+ -+ if (!dw_hdmi_check_output_type_changed(hdmi)) -+ return; -+ -+ if (!hdmi->bridge_is_on) -+ return; -+ -+ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); -+ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); -+ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_set_output_type); -+ -+bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi) -+{ -+ return hdmi->sink_is_hdmi; -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_get_output_whether_hdmi); -+ -+int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi) -+{ -+ return hdmi->support_hdmi; -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_get_output_type_cap); -+ - static void dw_hdmi_connector_force(struct drm_connector *connector) - { - struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, - connector); - - mutex_lock(&hdmi->mutex); -+ -+ if (hdmi->force != connector->force) { -+ if (!hdmi->disabled && connector->force == DRM_FORCE_OFF) -+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, -+ false); -+ else if (hdmi->disabled && connector->force == DRM_FORCE_ON) -+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, -+ true); -+ } -+ - hdmi->force = connector->force; - dw_hdmi_update_power(hdmi); - dw_hdmi_update_phy_mask(hdmi); -@@ -2549,15 +3161,98 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = { - .destroy = drm_connector_cleanup, - .force = dw_hdmi_connector_force, - .reset = drm_atomic_helper_connector_reset, -+ .set_property = dw_hdmi_connector_set_property, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -+ .atomic_set_property = dw_hdmi_atomic_connector_set_property, -+ .atomic_get_property = dw_hdmi_atomic_connector_get_property, - }; - - static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { - .get_modes = dw_hdmi_connector_get_modes, -+ .best_encoder = dw_hdmi_connector_best_encoder, - .atomic_check = dw_hdmi_connector_atomic_check, - }; - -+static void dw_hdmi_attach_properties(struct dw_hdmi *hdmi) -+{ -+ unsigned int color = MEDIA_BUS_FMT_RGB888_1X24; -+ int video_mapping, colorspace; -+ enum drm_connector_status connect_status = -+ hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); -+ const struct dw_hdmi_property_ops *ops = -+ hdmi->plat_data->property_ops; -+ -+ if (connect_status == connector_status_connected) { -+ video_mapping = (hdmi_readb(hdmi, HDMI_TX_INVID0) & -+ HDMI_TX_INVID0_VIDEO_MAPPING_MASK); -+ colorspace = (hdmi_readb(hdmi, HDMI_FC_AVICONF0) & -+ HDMI_FC_AVICONF0_PIX_FMT_MASK); -+ switch (video_mapping) { -+ case 0x01: -+ color = MEDIA_BUS_FMT_RGB888_1X24; -+ break; -+ case 0x03: -+ color = MEDIA_BUS_FMT_RGB101010_1X30; -+ break; -+ case 0x09: -+ if (colorspace == HDMI_COLORSPACE_YUV420) -+ color = MEDIA_BUS_FMT_UYYVYY8_0_5X24; -+ else if (colorspace == HDMI_COLORSPACE_YUV422) -+ color = MEDIA_BUS_FMT_UYVY8_1X16; -+ else -+ color = MEDIA_BUS_FMT_YUV8_1X24; -+ break; -+ case 0x0b: -+ if (colorspace == HDMI_COLORSPACE_YUV420) -+ color = MEDIA_BUS_FMT_UYYVYY10_0_5X30; -+ else if (colorspace == HDMI_COLORSPACE_YUV422) -+ color = MEDIA_BUS_FMT_UYVY10_1X20; -+ else -+ color = MEDIA_BUS_FMT_YUV10_1X30; -+ break; -+ case 0x14: -+ color = MEDIA_BUS_FMT_UYVY10_1X20; -+ break; -+ case 0x16: -+ color = MEDIA_BUS_FMT_UYVY8_1X16; -+ break; -+ default: -+ color = MEDIA_BUS_FMT_RGB888_1X24; -+ dev_err(hdmi->dev, "unexpected mapping: 0x%x\n", -+ video_mapping); -+ } -+ -+ hdmi->hdmi_data.enc_in_bus_format = color; -+ hdmi->hdmi_data.enc_out_bus_format = color; -+ /* -+ * input format will be set as yuv444 when output -+ * format is yuv420 -+ */ -+ if (color == MEDIA_BUS_FMT_UYVY10_1X20) -+ hdmi->hdmi_data.enc_in_bus_format = -+ MEDIA_BUS_FMT_YUV10_1X30; -+ else if (color == MEDIA_BUS_FMT_UYVY8_1X16) -+ hdmi->hdmi_data.enc_in_bus_format = -+ MEDIA_BUS_FMT_YUV8_1X24; -+ } -+ -+ if (ops && ops->attach_properties) -+ return ops->attach_properties(&hdmi->connector, -+ color, hdmi->version, -+ hdmi->plat_data->phy_data); -+} -+ -+static void dw_hdmi_destroy_properties(struct dw_hdmi *hdmi) -+{ -+ const struct dw_hdmi_property_ops *ops = -+ hdmi->plat_data->property_ops; -+ -+ if (ops && ops->destroy_properties) -+ return ops->destroy_properties(&hdmi->connector, -+ hdmi->plat_data->phy_data); -+} -+ - static int dw_hdmi_connector_create(struct dw_hdmi *hdmi) - { - struct drm_connector *connector = &hdmi->connector; -@@ -2589,10 +3284,13 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi) - drm_connector_attach_max_bpc_property(connector, 8, 16); - - if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe) -- drm_connector_attach_hdr_output_metadata_property(connector); -+ drm_object_attach_property(&connector->base, -+ connector->dev->mode_config.hdr_output_metadata_property, 0); - - drm_connector_attach_encoder(connector, hdmi->bridge.encoder); - -+ dw_hdmi_attach_properties(hdmi); -+ - cec_fill_conn_info_from_drm(&conn_info, connector); - - notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info); -@@ -2654,9 +3352,8 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, - if (!output_fmts) - return NULL; - -- /* If dw-hdmi is the first or only bridge, avoid negociating with ourselves */ -- if (list_is_singular(&bridge->encoder->bridge_chain) || -- list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain)) { -+ /* If dw-hdmi is the only bridge, avoid negociating with ourselves */ -+ if (list_is_singular(&bridge->encoder->bridge_chain)) { - *num_output_fmts = 1; - output_fmts[0] = MEDIA_BUS_FMT_FIXED; - -@@ -2869,16 +3566,36 @@ static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge, - struct drm_connector_state *conn_state) - { - struct dw_hdmi *hdmi = bridge->driver_private; -+ void *data = hdmi->plat_data->phy_data; - -- hdmi->hdmi_data.enc_out_bus_format = -- bridge_state->output_bus_cfg.format; -+ if (bridge_state->output_bus_cfg.format == MEDIA_BUS_FMT_FIXED) { -+ if (hdmi->plat_data->get_output_bus_format) -+ hdmi->hdmi_data.enc_out_bus_format = -+ hdmi->plat_data->get_output_bus_format(data); -+ else -+ hdmi->hdmi_data.enc_out_bus_format = -+ MEDIA_BUS_FMT_RGB888_1X24; -+ -+ if (hdmi->plat_data->get_input_bus_format) -+ hdmi->hdmi_data.enc_in_bus_format = -+ hdmi->plat_data->get_input_bus_format(data); -+ else if (hdmi->plat_data->input_bus_format) -+ hdmi->hdmi_data.enc_in_bus_format = -+ hdmi->plat_data->input_bus_format; -+ else -+ hdmi->hdmi_data.enc_in_bus_format = -+ MEDIA_BUS_FMT_RGB888_1X24; -+ } else { -+ hdmi->hdmi_data.enc_out_bus_format = -+ bridge_state->output_bus_cfg.format; - -- hdmi->hdmi_data.enc_in_bus_format = -- bridge_state->input_bus_cfg.format; -+ hdmi->hdmi_data.enc_in_bus_format = -+ bridge_state->input_bus_cfg.format; - -- dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n", -- bridge_state->input_bus_cfg.format, -- bridge_state->output_bus_cfg.format); -+ dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n", -+ bridge_state->input_bus_cfg.format, -+ bridge_state->output_bus_cfg.format); -+ } - - return 0; - } -@@ -2887,10 +3604,21 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge, - enum drm_bridge_attach_flags flags) - { - struct dw_hdmi *hdmi = bridge->driver_private; -+ int ret; - - if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) -- return drm_bridge_attach(bridge->encoder, hdmi->next_bridge, -- bridge, flags); -+ return 0; -+ -+ if (hdmi->next_bridge) { -+ hdmi->next_bridge->encoder = bridge->encoder; -+ ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, bridge, flags); -+ if (ret) { -+ DRM_ERROR("Failed to attach bridge with dw-hdmi\n"); -+ return ret; -+ } -+ -+ return 0; -+ } - - return dw_hdmi_connector_create(hdmi); - } -@@ -2914,14 +3642,12 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge, - const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; - enum drm_mode_status mode_status = MODE_OK; - -- /* We don't support double-clocked modes */ -- if (mode->flags & DRM_MODE_FLAG_DBLCLK) -- return MODE_BAD; -+ if (hdmi->next_bridge) -+ return MODE_OK; - - if (pdata->mode_valid) -- mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info, -- mode); -- -+ mode_status = pdata->mode_valid(hdmi, pdata->priv_data, -+ info, mode); - return mode_status; - } - -@@ -2934,7 +3660,7 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, - mutex_lock(&hdmi->mutex); - - /* Store the display mode for plugin/DKMS poweron events */ -- drm_mode_copy(&hdmi->previous_mode, mode); -+ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); - - mutex_unlock(&hdmi->mutex); - } -@@ -3004,6 +3730,12 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { - .get_edid = dw_hdmi_bridge_get_edid, - }; - -+void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap) -+{ -+ hdmi->cec_adap = adap; -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_set_cec_adap); -+ - /* ----------------------------------------------------------------------------- - * IRQ Handling - */ -@@ -3029,7 +3761,7 @@ static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi) - static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id) - { - struct dw_hdmi *hdmi = dev_id; -- u8 intr_stat; -+ u8 intr_stat, hdcp_stat; - irqreturn_t ret = IRQ_NONE; - - if (hdmi->i2c) -@@ -3041,6 +3773,13 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id) - return IRQ_WAKE_THREAD; - } - -+ hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT); -+ if (hdcp_stat) { -+ dev_dbg(hdmi->dev, "HDCP irq %#x\n", hdcp_stat); -+ hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK); -+ return IRQ_WAKE_THREAD; -+ } -+ - return ret; - } - -@@ -3048,7 +3787,7 @@ void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense) - { - mutex_lock(&hdmi->mutex); - -- if (!hdmi->force) { -+ if (!hdmi->force && !hdmi->force_logo) { - /* - * If the RX sense status indicates we're disconnected, - * clear the software rxsense status. -@@ -3075,7 +3814,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_setup_rx_sense); - static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) - { - struct dw_hdmi *hdmi = dev_id; -- u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat; -+ u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat, hdcp_stat; - enum drm_connector_status status = connector_status_unknown; - - intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); -@@ -3134,10 +3873,21 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) - } - } - -- hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); -- hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), -- HDMI_IH_MUTE_PHY_STAT0); -+ check_hdmi_irq(hdmi, intr_stat, phy_int_pol); - -+ hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); -+ if (!hdmi->next_bridge) -+ hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | -+ HDMI_IH_PHY_STAT0_RX_SENSE), -+ HDMI_IH_MUTE_PHY_STAT0); -+ -+ hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT); -+ if (hdcp_stat) { -+ if (hdmi->hdcp) -+ hdmi->hdcp->hdcp_isr(hdmi->hdcp, hdcp_stat); -+ hdmi_writeb(hdmi, hdcp_stat, HDMI_A_APIINTCLR); -+ hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK); -+ } - return IRQ_HANDLED; - } - -@@ -3271,72 +4021,372 @@ static void dw_hdmi_init_hw(struct dw_hdmi *hdmi) - * Even if we are using a separate i2c adapter doing this doesn't - * hurt. - */ -- dw_hdmi_i2c_init(hdmi); -+ if (hdmi->i2c) -+ dw_hdmi_i2c_init(hdmi); - - if (hdmi->phy.ops->setup_hpd) - hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); - } - --/* ----------------------------------------------------------------------------- -- * Probe/remove API, used from platforms based on the DRM bridge API. -- */ -- --static int dw_hdmi_parse_dt(struct dw_hdmi *hdmi) -+static int dw_hdmi_status_show(struct seq_file *s, void *v) - { -- struct device_node *endpoint; -- struct device_node *remote; -+ struct dw_hdmi *hdmi = s->private; -+ u32 val; -+ -+ seq_puts(s, "PHY: "); -+ if (!hdmi->phy.enabled) { -+ seq_puts(s, "disabled\n"); -+ return 0; -+ } -+ seq_puts(s, "enabled\t\t\tMode: "); -+ if (hdmi->sink_is_hdmi) -+ seq_puts(s, "HDMI\n"); -+ else -+ seq_puts(s, "DVI\n"); -+ if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000) -+ val = hdmi->hdmi_data.video_mode.mtmdsclock / 4; -+ else -+ val = hdmi->hdmi_data.video_mode.mtmdsclock; -+ seq_printf(s, "Pixel Clk: %uHz\t\tTMDS Clk: %uHz\n", -+ hdmi->hdmi_data.video_mode.mpixelclock, val); -+ seq_puts(s, "Color Format: "); -+ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) -+ seq_puts(s, "RGB"); -+ else if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) -+ seq_puts(s, "YUV444"); -+ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) -+ seq_puts(s, "YUV422"); -+ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) -+ seq_puts(s, "YUV420"); -+ else -+ seq_puts(s, "UNKNOWN"); -+ val = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); -+ seq_printf(s, "\t\tColor Depth: %d bit\n", val); -+ seq_puts(s, "Colorimetry: "); -+ switch (hdmi->hdmi_data.enc_out_encoding) { -+ case V4L2_YCBCR_ENC_601: -+ seq_puts(s, "ITU.BT601"); -+ break; -+ case V4L2_YCBCR_ENC_709: -+ seq_puts(s, "ITU.BT709"); -+ break; -+ case V4L2_YCBCR_ENC_BT2020: -+ seq_puts(s, "ITU.BT2020"); -+ break; -+ default: /* Carries no data */ -+ seq_puts(s, "ITU.BT601"); -+ break; -+ } -+ -+ seq_puts(s, "\t\tEOTF: "); - -- if (!hdmi->plat_data->output_port) -+ if (hdmi->version < 0x211a) { -+ seq_puts(s, "Unsupported\n"); - return 0; -+ } - -- endpoint = of_graph_get_endpoint_by_regs(hdmi->dev->of_node, -- hdmi->plat_data->output_port, -- -1); -- if (!endpoint) { -- /* -- * On platforms whose bindings don't make the output port -- * mandatory (such as Rockchip) the plat_data->output_port -- * field isn't set, so it's safe to make this a fatal error. -- */ -- dev_err(hdmi->dev, "Missing endpoint in port@%u\n", -- hdmi->plat_data->output_port); -- return -ENODEV; -+ val = hdmi_readb(hdmi, HDMI_FC_PACKET_TX_EN); -+ if (!(val & HDMI_FC_PACKET_TX_EN_DRM_MASK)) { -+ seq_puts(s, "Off\n"); -+ return 0; - } - -- remote = of_graph_get_remote_port_parent(endpoint); -- of_node_put(endpoint); -- if (!remote) { -- dev_err(hdmi->dev, "Endpoint in port@%u unconnected\n", -- hdmi->plat_data->output_port); -- return -ENODEV; -+ switch (hdmi_readb(hdmi, HDMI_FC_DRM_PB0)) { -+ case HDMI_EOTF_TRADITIONAL_GAMMA_SDR: -+ seq_puts(s, "SDR"); -+ break; -+ case HDMI_EOTF_TRADITIONAL_GAMMA_HDR: -+ seq_puts(s, "HDR"); -+ break; -+ case HDMI_EOTF_SMPTE_ST2084: -+ seq_puts(s, "ST2084"); -+ break; -+ case HDMI_EOTF_BT_2100_HLG: -+ seq_puts(s, "HLG"); -+ break; -+ default: -+ seq_puts(s, "Not Defined\n"); -+ return 0; - } - -- if (!of_device_is_available(remote)) { -- dev_err(hdmi->dev, "port@%u remote device is disabled\n", -- hdmi->plat_data->output_port); -- of_node_put(remote); -- return -ENODEV; -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB3) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB2); -+ seq_printf(s, "\nx0: %d", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB5) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB4); -+ seq_printf(s, "\t\t\t\ty0: %d\n", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB7) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB6); -+ seq_printf(s, "x1: %d", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB9) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB8); -+ seq_printf(s, "\t\t\t\ty1: %d\n", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB11) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB10); -+ seq_printf(s, "x2: %d", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB13) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB12); -+ seq_printf(s, "\t\t\t\ty2: %d\n", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB15) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB14); -+ seq_printf(s, "white x: %d", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB17) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB16); -+ seq_printf(s, "\t\t\twhite y: %d\n", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB19) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB18); -+ seq_printf(s, "max lum: %d", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB21) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB20); -+ seq_printf(s, "\t\t\tmin lum: %d\n", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB23) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB22); -+ seq_printf(s, "max cll: %d", val); -+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB25) << 8; -+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB24); -+ seq_printf(s, "\t\t\tmax fall: %d\n", val); -+ return 0; -+} -+ -+static int dw_hdmi_status_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, dw_hdmi_status_show, inode->i_private); -+} -+ -+static const struct file_operations dw_hdmi_status_fops = { -+ .owner = THIS_MODULE, -+ .open = dw_hdmi_status_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+#include -+#include -+#include -+ -+struct dw_hdmi_reg_table { -+ int reg_base; -+ int reg_end; -+}; -+ -+static const struct dw_hdmi_reg_table hdmi_reg_table[] = { -+ {HDMI_DESIGN_ID, HDMI_CONFIG3_ID}, -+ {HDMI_IH_FC_STAT0, HDMI_IH_MUTE}, -+ {HDMI_TX_INVID0, HDMI_TX_BCBDATA1}, -+ {HDMI_VP_STATUS, HDMI_VP_POL}, -+ {HDMI_FC_INVIDCONF, HDMI_FC_DBGTMDS2}, -+ {HDMI_PHY_CONF0, HDMI_PHY_POL0}, -+ {HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR}, -+ {HDMI_AUD_CONF0, 0x3624}, -+ {HDMI_MC_SFRDIV, HDMI_MC_HEACPHY_RST}, -+ {HDMI_CSC_CFG, HDMI_CSC_COEF_C4_LSB}, -+ {HDMI_A_HDCPCFG0, 0x52bb}, -+ {0x7800, 0x7818}, -+ {0x7900, 0x790e}, -+ {HDMI_CEC_CTRL, HDMI_CEC_WKUPCTRL}, -+ {HDMI_I2CM_SLAVE, 0x7e31}, -+}; -+ -+static int dw_hdmi_ctrl_show(struct seq_file *s, void *v) -+{ -+ struct dw_hdmi *hdmi = s->private; -+ u32 i = 0, j = 0, val = 0; -+ -+ seq_puts(s, "\n>>>hdmi_ctl reg "); -+ for (i = 0; i < 16; i++) -+ seq_printf(s, " %2x", i); -+ seq_puts(s, "\n---------------------------------------------------"); -+ -+ for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) { -+ for (j = hdmi_reg_table[i].reg_base; -+ j <= hdmi_reg_table[i].reg_end; j++) { -+ val = hdmi_readb(hdmi, j); -+ if ((j - hdmi_reg_table[i].reg_base) % 16 == 0) -+ seq_printf(s, "\n>>>hdmi_ctl %04x:", j); -+ seq_printf(s, " %02x", val); -+ } - } -+ seq_puts(s, "\n---------------------------------------------------\n"); - -- hdmi->next_bridge = of_drm_find_bridge(remote); -- of_node_put(remote); -- if (!hdmi->next_bridge) -- return -EPROBE_DEFER; -+ return 0; -+} -+ -+static int dw_hdmi_ctrl_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, dw_hdmi_ctrl_show, inode->i_private); -+} -+ -+static ssize_t -+dw_hdmi_ctrl_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct dw_hdmi *hdmi = -+ ((struct seq_file *)file->private_data)->private; -+ u32 reg, val; -+ char kbuf[25]; -+ -+ if (copy_from_user(kbuf, buf, count)) -+ return -EFAULT; -+ if (sscanf(kbuf, "%x%x", ®, &val) == -1) -+ return -EFAULT; -+ if (reg > HDMI_I2CM_FS_SCL_LCNT_0_ADDR) { -+ dev_err(hdmi->dev, "it is no a hdmi register\n"); -+ return count; -+ } -+ dev_info(hdmi->dev, "/**********hdmi register config******/"); -+ dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val); -+ hdmi_writeb(hdmi, val, reg); -+ return count; -+} - -+static const struct file_operations dw_hdmi_ctrl_fops = { -+ .owner = THIS_MODULE, -+ .open = dw_hdmi_ctrl_open, -+ .read = seq_read, -+ .write = dw_hdmi_ctrl_write, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static int dw_hdmi_phy_show(struct seq_file *s, void *v) -+{ -+ struct dw_hdmi *hdmi = s->private; -+ u32 i; -+ -+ seq_puts(s, "\n>>>hdmi_phy reg "); -+ for (i = 0; i < 0x28; i++) -+ seq_printf(s, "regs %02x val %04x\n", -+ i, hdmi_phy_i2c_read(hdmi, i)); - return 0; - } - --bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi) -+static int dw_hdmi_phy_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, dw_hdmi_phy_show, inode->i_private); -+} -+ -+static ssize_t -+dw_hdmi_phy_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct dw_hdmi *hdmi = -+ ((struct seq_file *)file->private_data)->private; -+ u32 reg, val; -+ char kbuf[25]; -+ -+ if (copy_from_user(kbuf, buf, count)) -+ return -EFAULT; -+ if (sscanf(kbuf, "%x%x", ®, &val) == -1) -+ return -EFAULT; -+ if (reg > 0x28) { -+ dev_err(hdmi->dev, "it is not a hdmi phy register\n"); -+ return count; -+ } -+ dev_info(hdmi->dev, "/*******hdmi phy register config******/"); -+ dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val); -+ dw_hdmi_phy_i2c_write(hdmi, val, reg); -+ return count; -+} -+ -+static const struct file_operations dw_hdmi_phy_fops = { -+ .owner = THIS_MODULE, -+ .open = dw_hdmi_phy_open, -+ .read = seq_read, -+ .write = dw_hdmi_phy_write, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi *hdmi) -+{ -+ hdmi->debugfs_dir = debugfs_create_dir("dw-hdmi", NULL); -+ if (IS_ERR(hdmi->debugfs_dir)) { -+ dev_err(dev, "failed to create debugfs dir!\n"); -+ return; -+ } -+ debugfs_create_file("status", 0400, hdmi->debugfs_dir, -+ hdmi, &dw_hdmi_status_fops); -+ debugfs_create_file("ctrl", 0400, hdmi->debugfs_dir, -+ hdmi, &dw_hdmi_ctrl_fops); -+ debugfs_create_file("phy", 0400, hdmi->debugfs_dir, -+ hdmi, &dw_hdmi_phy_fops); -+} -+ -+static void dw_hdmi_register_hdcp(struct device *dev, struct dw_hdmi *hdmi, -+ u32 val, bool hdcp1x_enable) -+{ -+ struct dw_hdcp hdmi_hdcp = { -+ .hdmi = hdmi, -+ .write = hdmi_writeb, -+ .read = hdmi_readb, -+ .regs = hdmi->regs, -+ .reg_io_width = val, -+ .enable = hdcp1x_enable, -+ }; -+ struct platform_device_info hdcp_device_info = { -+ .parent = dev, -+ .id = PLATFORM_DEVID_AUTO, -+ .res = NULL, -+ .num_res = 0, -+ .name = DW_HDCP_DRIVER_NAME, -+ .data = &hdmi_hdcp, -+ .size_data = sizeof(hdmi_hdcp), -+ .dma_mask = DMA_BIT_MASK(32), -+ }; -+ -+ hdmi->hdcp_dev = platform_device_register_full(&hdcp_device_info); -+ if (IS_ERR(hdmi->hdcp_dev)) -+ dev_err(dev, "failed to register hdcp!\n"); -+ else -+ hdmi->hdcp = hdmi->hdcp_dev->dev.platform_data; -+} -+ -+static int get_force_logo_property(struct dw_hdmi *hdmi) - { -- return hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format); -+ struct device_node *dss; -+ struct device_node *route; -+ struct device_node *route_hdmi; -+ -+ dss = of_find_node_by_name(NULL, "display-subsystem"); -+ if (!dss) { -+ dev_err(hdmi->dev, "can't find display-subsystem\n"); -+ return -ENODEV; -+ } -+ -+ route = of_find_node_by_name(dss, "route"); -+ if (!route) { -+ dev_err(hdmi->dev, "can't find route\n"); -+ of_node_put(dss); -+ return -ENODEV; -+ } -+ of_node_put(dss); -+ -+ route_hdmi = of_find_node_by_name(route, "route-hdmi"); -+ if (!route_hdmi) { -+ dev_err(hdmi->dev, "can't find route-hdmi\n"); -+ of_node_put(route); -+ return -ENODEV; -+ } -+ of_node_put(route); -+ -+ hdmi->force_logo = -+ of_property_read_bool(route_hdmi, "force-output"); -+ -+ of_node_put(route_hdmi); -+ -+ return 0; - } --EXPORT_SYMBOL_GPL(dw_hdmi_bus_fmt_is_420); - -+/* ----------------------------------------------------------------------------- -+ * Probe/remove API, used from platforms based on the DRM bridge API. -+ */ - struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - const struct dw_hdmi_plat_data *plat_data) - { - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; -+ struct device_node *endpoint; - struct platform_device_info pdevinfo; - struct device_node *ddc_node; - struct dw_hdmi_cec_data cec; -@@ -3349,15 +4399,16 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - u8 prod_id1; - u8 config0; - u8 config3; -+ bool hdcp1x_enable = 0; - - hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return ERR_PTR(-ENOMEM); - -+ hdmi->connector.stereo_allowed = 1; - hdmi->plat_data = plat_data; - hdmi->dev = dev; - hdmi->sample_rate = 48000; -- hdmi->channels = 2; - hdmi->disabled = true; - hdmi->rxsense = true; - hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE); -@@ -3369,10 +4420,6 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - mutex_init(&hdmi->cec_notifier_mutex); - spin_lock_init(&hdmi->audio_lock); - -- ret = dw_hdmi_parse_dt(hdmi); -- if (ret < 0) -- return ERR_PTR(ret); -- - ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); - if (ddc_node) { - hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node); -@@ -3489,7 +4536,24 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without", - hdmi->phy.name); - -- dw_hdmi_init_hw(hdmi); -+ ret = get_force_logo_property(hdmi); -+ if (ret) -+ goto err_iahb; -+ -+ hdmi->initialized = false; -+ ret = hdmi_readb(hdmi, HDMI_PHY_STAT0); -+ if (((ret & HDMI_PHY_TX_PHY_LOCK) && (ret & HDMI_PHY_HPD) && -+ hdmi_readb(hdmi, HDMI_FC_EXCTRLDUR)) || hdmi->force_logo) { -+ hdmi->mc_clkdis = hdmi_readb(hdmi, HDMI_MC_CLKDIS); -+ hdmi->disabled = false; -+ hdmi->bridge_is_on = true; -+ hdmi->phy.enabled = true; -+ hdmi->initialized = true; -+ } else if (ret & HDMI_PHY_TX_PHY_LOCK) { -+ hdmi->phy.ops->disable(hdmi, hdmi->phy.data); -+ } -+ -+ init_hpd_work(hdmi); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { -@@ -3497,6 +4561,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - goto err_iahb; - } - -+ hdmi->irq = irq; - ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, - dw_hdmi_irq, IRQF_SHARED, - dev_name(dev), hdmi); -@@ -3532,8 +4597,20 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - hdmi->ddc = dw_hdmi_i2c_adapter(hdmi); - if (IS_ERR(hdmi->ddc)) - hdmi->ddc = NULL; -+ /* -+ * Read high and low time from device tree. If not available use -+ * the default timing scl clock rate is about 99.6KHz. -+ */ -+ if (of_property_read_u32(np, "ddc-i2c-scl-high-time-ns", -+ &hdmi->i2c->scl_high_ns)) -+ hdmi->i2c->scl_high_ns = 4708; -+ if (of_property_read_u32(np, "ddc-i2c-scl-low-time-ns", -+ &hdmi->i2c->scl_low_ns)) -+ hdmi->i2c->scl_low_ns = 4916; - } - -+ dw_hdmi_init_hw(hdmi); -+ - hdmi->bridge.driver_private = hdmi; - hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; - hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID -@@ -3542,6 +4619,30 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - hdmi->bridge.ddc = hdmi->ddc; - hdmi->bridge.of_node = pdev->dev.of_node; - -+ endpoint = of_graph_get_endpoint_by_regs(hdmi->dev->of_node, 1, -1); -+ if (endpoint && of_device_is_available(endpoint)) { -+ struct device_node *remote; -+ -+ remote = of_graph_get_remote_port_parent(endpoint); -+ of_node_put(endpoint); -+ if (!remote || !of_device_is_available(remote)) { -+ of_node_put(remote); -+ ret = -ENODEV; -+ goto err_iahb; -+ } -+ -+ hdmi->next_bridge = of_drm_find_bridge(remote); -+ of_node_put(remote); -+ if (!hdmi->next_bridge) { -+ dev_err(hdmi->dev, "can't find next bridge\n"); -+ ret = -EPROBE_DEFER; -+ goto err_iahb; -+ } -+ -+ hdmi->sink_is_hdmi = true; -+ hdmi->sink_has_audio = true; -+ } -+ - memset(&pdevinfo, 0, sizeof(pdevinfo)); - pdevinfo.parent = dev; - pdevinfo.id = PLATFORM_DEVID_AUTO; -@@ -3556,7 +4657,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - audio.base = hdmi->regs; - audio.irq = irq; - audio.hdmi = hdmi; -- audio.get_eld = hdmi_audio_get_eld; -+ audio.eld = hdmi->connector.eld; - hdmi->enable_audio = dw_hdmi_ahb_audio_enable; - hdmi->disable_audio = dw_hdmi_ahb_audio_disable; - -@@ -3569,7 +4670,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - struct dw_hdmi_i2s_audio_data audio; - - audio.hdmi = hdmi; -- audio.get_eld = hdmi_audio_get_eld; -+ audio.eld = hdmi->connector.eld; - audio.write = hdmi_writeb; - audio.read = hdmi_readb; - hdmi->enable_audio = dw_hdmi_i2s_audio_enable; -@@ -3580,27 +4681,9 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - pdevinfo.size_data = sizeof(audio); - pdevinfo.dma_mask = DMA_BIT_MASK(32); - hdmi->audio = platform_device_register_full(&pdevinfo); -- } else if (iores && config3 & HDMI_CONFIG3_GPAUD) { -- struct dw_hdmi_audio_data audio; -- -- audio.phys = iores->start; -- audio.base = hdmi->regs; -- audio.irq = irq; -- audio.hdmi = hdmi; -- audio.get_eld = hdmi_audio_get_eld; -- -- hdmi->enable_audio = dw_hdmi_gp_audio_enable; -- hdmi->disable_audio = dw_hdmi_gp_audio_disable; -- -- pdevinfo.name = "dw-hdmi-gp-audio"; -- pdevinfo.id = PLATFORM_DEVID_NONE; -- pdevinfo.data = &audio; -- pdevinfo.size_data = sizeof(audio); -- pdevinfo.dma_mask = DMA_BIT_MASK(32); -- hdmi->audio = platform_device_register_full(&pdevinfo); - } - -- if (!plat_data->disable_cec && (config0 & HDMI_CONFIG0_CEC)) { -+ if (config0 & HDMI_CONFIG0_CEC) { - cec.hdmi = hdmi; - cec.ops = &dw_hdmi_cec_ops; - cec.irq = irq; -@@ -3613,17 +4696,53 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - hdmi->cec = platform_device_register_full(&pdevinfo); - } - -+ hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, dw_hdmi_cable); -+ if (IS_ERR(hdmi->extcon)) { -+ ret = PTR_ERR(hdmi->extcon); -+ dev_err(hdmi->dev, "allocate extcon failed: %d\n", ret); -+ goto err_iahb; -+ } -+ -+ ret = devm_extcon_dev_register(hdmi->dev, hdmi->extcon); -+ if (ret) { -+ dev_err(hdmi->dev, "failed to register extcon: %d\n", -+ ret); -+ goto err_iahb; -+ } -+ -+ ret = extcon_set_property_capability(hdmi->extcon, EXTCON_DISP_HDMI, -+ EXTCON_PROP_DISP_HPD); -+ if (ret) { -+ dev_err(hdmi->dev, -+ "failed to set USB property capability: %d\n", -+ ret); -+ goto err_iahb; -+ } -+ - drm_bridge_add(&hdmi->bridge); - -+ dw_hdmi_register_debugfs(dev, hdmi); -+ -+ if (of_property_read_bool(np, "scramble-low-rates")) -+ hdmi->scramble_low_rates = true; -+ -+ if (of_property_read_bool(np, "hdcp1x-enable")) -+ hdcp1x_enable = 1; -+ dw_hdmi_register_hdcp(dev, hdmi, val, hdcp1x_enable); -+ - return hdmi; - - err_iahb: - clk_disable_unprepare(hdmi->iahb_clk); -- clk_disable_unprepare(hdmi->cec_clk); -+ if (hdmi->cec_clk) -+ clk_disable_unprepare(hdmi->cec_clk); - err_isfr: - clk_disable_unprepare(hdmi->isfr_clk); - err_res: -- i2c_put_adapter(hdmi->ddc); -+ if (hdmi->i2c) -+ i2c_del_adapter(&hdmi->i2c->adap); -+ else -+ i2c_put_adapter(hdmi->ddc); - - return ERR_PTR(ret); - } -@@ -3631,19 +4750,39 @@ EXPORT_SYMBOL_GPL(dw_hdmi_probe); - - void dw_hdmi_remove(struct dw_hdmi *hdmi) - { -+ if (hdmi->irq) -+ disable_irq(hdmi->irq); -+ -+ cancel_delayed_work(&hdmi->work); -+ flush_workqueue(hdmi->workqueue); -+ destroy_workqueue(hdmi->workqueue); -+ -+ debugfs_remove_recursive(hdmi->debugfs_dir); -+ - drm_bridge_remove(&hdmi->bridge); - - if (hdmi->audio && !IS_ERR(hdmi->audio)) - platform_device_unregister(hdmi->audio); -+ if (hdmi->hdcp_dev && !IS_ERR(hdmi->hdcp_dev)) -+ platform_device_unregister(hdmi->hdcp_dev); - if (!IS_ERR(hdmi->cec)) - platform_device_unregister(hdmi->cec); - - /* Disable all interrupts */ - hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); - -+ if (!hdmi->next_bridge) { -+ dw_hdmi_destroy_properties(hdmi); -+ hdmi->connector.funcs->destroy(&hdmi->connector); -+ } -+ -+ if (hdmi->bridge.encoder) -+ hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder); -+ - clk_disable_unprepare(hdmi->iahb_clk); - clk_disable_unprepare(hdmi->isfr_clk); -- clk_disable_unprepare(hdmi->cec_clk); -+ if (hdmi->cec_clk) -+ clk_disable_unprepare(hdmi->cec_clk); - - if (hdmi->i2c) - i2c_del_adapter(&hdmi->i2c->adap); -@@ -3657,7 +4796,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_remove); - */ - struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, - struct drm_encoder *encoder, -- const struct dw_hdmi_plat_data *plat_data) -+ struct dw_hdmi_plat_data *plat_data) - { - struct dw_hdmi *hdmi; - int ret; -@@ -3669,9 +4808,13 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, - ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0); - if (ret) { - dw_hdmi_remove(hdmi); -+ DRM_ERROR("Failed to initialize bridge with drm\n"); - return ERR_PTR(ret); - } - -+ if (!hdmi->next_bridge) -+ plat_data->connector = &hdmi->connector; -+ - return hdmi; - } - EXPORT_SYMBOL_GPL(dw_hdmi_bind); -@@ -3682,9 +4825,87 @@ void dw_hdmi_unbind(struct dw_hdmi *hdmi) - } - EXPORT_SYMBOL_GPL(dw_hdmi_unbind); - -+static void dw_hdmi_reg_initial(struct dw_hdmi *hdmi) -+{ -+ if (hdmi_readb(hdmi, HDMI_IH_MUTE)) { -+ initialize_hdmi_ih_mutes(hdmi); -+ /* unmute cec irq */ -+ hdmi_writeb(hdmi, 0x68, HDMI_IH_MUTE_CEC_STAT0); -+ -+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, -+ HDMI_PHY_I2CM_INT_ADDR); -+ -+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | -+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, -+ HDMI_PHY_I2CM_CTLINT_ADDR); -+ -+ if (!hdmi->next_bridge) { -+ hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, -+ HDMI_PHY_POL0); -+ hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); -+ hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | -+ HDMI_IH_PHY_STAT0_RX_SENSE), -+ HDMI_IH_MUTE_PHY_STAT0); -+ } -+ } -+} -+ -+void dw_hdmi_suspend(struct dw_hdmi *hdmi) -+{ -+ if (!hdmi) -+ return; -+ -+ mutex_lock(&hdmi->mutex); -+ -+ /* -+ * When system shutdown, hdmi should be disabled. -+ * When system suspend, dw_hdmi_bridge_disable will disable hdmi first. -+ * To prevent duplicate operation, we should determine whether hdmi -+ * has been disabled. -+ */ -+ if (!hdmi->disabled) { -+ hdmi->disabled = true; -+ dw_hdmi_update_power(hdmi); -+ dw_hdmi_update_phy_mask(hdmi); -+ } -+ mutex_unlock(&hdmi->mutex); -+ -+ if (hdmi->irq) -+ disable_irq(hdmi->irq); -+ cancel_delayed_work(&hdmi->work); -+ flush_workqueue(hdmi->workqueue); -+ pinctrl_pm_select_sleep_state(hdmi->dev); -+} -+EXPORT_SYMBOL_GPL(dw_hdmi_suspend); -+ - void dw_hdmi_resume(struct dw_hdmi *hdmi) - { -- dw_hdmi_init_hw(hdmi); -+ if (!hdmi) -+ return; -+ -+ pinctrl_pm_select_default_state(hdmi->dev); -+ mutex_lock(&hdmi->mutex); -+ dw_hdmi_reg_initial(hdmi); -+ if (hdmi->i2c) -+ dw_hdmi_i2c_init(hdmi); -+ if (hdmi->irq) -+ enable_irq(hdmi->irq); -+ /* -+ * HDMI status maybe incorrect in the following condition: -+ * HDMI plug in -> system sleep -> HDMI plug out -> system wake up. -+ * At this time, cat /sys/class/drm/card 0-HDMI-A-1/status is connected. -+ * There is no hpd interrupt, because HDMI is powerdown during suspend. -+ * So we need check the current HDMI status in this case. -+ */ -+ if (hdmi->connector.status == connector_status_connected) { -+ if (hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data) == -+ connector_status_disconnected) { -+ hdmi->hpd_state = false; -+ mod_delayed_work(hdmi->workqueue, &hdmi->work, -+ msecs_to_jiffies(20)); -+ } -+ } -+ mutex_unlock(&hdmi->mutex); - } - EXPORT_SYMBOL_GPL(dw_hdmi_resume); - -diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h -index af43a0414b78..50973280048c 100644 ---- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h -+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h -@@ -158,17 +158,8 @@ - #define HDMI_FC_SPDDEVICEINF 0x1062 - #define HDMI_FC_AUDSCONF 0x1063 - #define HDMI_FC_AUDSSTAT 0x1064 --#define HDMI_FC_AUDSV 0x1065 --#define HDMI_FC_AUDSU 0x1066 --#define HDMI_FC_AUDSCHNLS0 0x1067 --#define HDMI_FC_AUDSCHNLS1 0x1068 --#define HDMI_FC_AUDSCHNLS2 0x1069 --#define HDMI_FC_AUDSCHNLS3 0x106A --#define HDMI_FC_AUDSCHNLS4 0x106B --#define HDMI_FC_AUDSCHNLS5 0x106C --#define HDMI_FC_AUDSCHNLS6 0x106D --#define HDMI_FC_AUDSCHNLS7 0x106E --#define HDMI_FC_AUDSCHNLS8 0x106F -+#define HDMI_FC_AUDSCHNLS7 0x106e -+#define HDMI_FC_AUDSCHNLS8 0x106f - #define HDMI_FC_DATACH0FILL 0x1070 - #define HDMI_FC_DATACH1FILL 0x1071 - #define HDMI_FC_DATACH2FILL 0x1072 -@@ -518,6 +509,51 @@ - #define HDMI_A_PRESETUP 0x501A - #define HDMI_A_SRM_BASE 0x5020 - -+/* CEC Engine Registers */ -+#define HDMI_CEC_CTRL 0x7D00 -+#define HDMI_CEC_STAT 0x7D01 -+#define HDMI_CEC_MASK 0x7D02 -+#define HDMI_CEC_POLARITY 0x7D03 -+#define HDMI_CEC_INT 0x7D04 -+#define HDMI_CEC_ADDR_L 0x7D05 -+#define HDMI_CEC_ADDR_H 0x7D06 -+#define HDMI_CEC_TX_CNT 0x7D07 -+#define HDMI_CEC_RX_CNT 0x7D08 -+#define HDMI_CEC_TX_DATA0 0x7D10 -+#define HDMI_CEC_TX_DATA1 0x7D11 -+#define HDMI_CEC_TX_DATA2 0x7D12 -+#define HDMI_CEC_TX_DATA3 0x7D13 -+#define HDMI_CEC_TX_DATA4 0x7D14 -+#define HDMI_CEC_TX_DATA5 0x7D15 -+#define HDMI_CEC_TX_DATA6 0x7D16 -+#define HDMI_CEC_TX_DATA7 0x7D17 -+#define HDMI_CEC_TX_DATA8 0x7D18 -+#define HDMI_CEC_TX_DATA9 0x7D19 -+#define HDMI_CEC_TX_DATA10 0x7D1a -+#define HDMI_CEC_TX_DATA11 0x7D1b -+#define HDMI_CEC_TX_DATA12 0x7D1c -+#define HDMI_CEC_TX_DATA13 0x7D1d -+#define HDMI_CEC_TX_DATA14 0x7D1e -+#define HDMI_CEC_TX_DATA15 0x7D1f -+#define HDMI_CEC_RX_DATA0 0x7D20 -+#define HDMI_CEC_RX_DATA1 0x7D21 -+#define HDMI_CEC_RX_DATA2 0x7D22 -+#define HDMI_CEC_RX_DATA3 0x7D23 -+#define HDMI_CEC_RX_DATA4 0x7D24 -+#define HDMI_CEC_RX_DATA5 0x7D25 -+#define HDMI_CEC_RX_DATA6 0x7D26 -+#define HDMI_CEC_RX_DATA7 0x7D27 -+#define HDMI_CEC_RX_DATA8 0x7D28 -+#define HDMI_CEC_RX_DATA9 0x7D29 -+#define HDMI_CEC_RX_DATA10 0x7D2a -+#define HDMI_CEC_RX_DATA11 0x7D2b -+#define HDMI_CEC_RX_DATA12 0x7D2c -+#define HDMI_CEC_RX_DATA13 0x7D2d -+#define HDMI_CEC_RX_DATA14 0x7D2e -+#define HDMI_CEC_RX_DATA15 0x7D2f -+#define HDMI_CEC_LOCK 0x7D30 -+#define HDMI_CEC_WKUPCTRL 0x7D31 -+ - /* I2C Master Registers (E-DDC) */ - #define HDMI_I2CM_SLAVE 0x7E00 - #define HDMI_I2CM_ADDRESS 0x7E01 -@@ -538,6 +574,7 @@ - #define HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x7E10 - #define HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x7E11 - #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 -+#define HDMI_I2CM_SDA_HOLD 0x7E13 - - enum { - /* PRODUCT_ID0 field values */ -@@ -851,6 +888,10 @@ enum { - HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00, - HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04, - -+/* HDMI_FC_GCP */ -+ HDMI_FC_GCP_SET_AVMUTE = 0x2, -+ HDMI_FC_GCP_CLEAR_AVMUTE = 0x1, -+ - /* FC_DBGFORCE field values */ - HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10, - HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, -@@ -859,9 +900,6 @@ enum { - HDMI_FC_DATAUTO0_VSD_MASK = 0x08, - HDMI_FC_DATAUTO0_VSD_OFFSET = 3, - --/* FC_DATAUTO3 field values */ -- HDMI_FC_DATAUTO3_GCP_AUTO = 0x04, -- - /* PHY_CONF0 field values */ - HDMI_PHY_CONF0_PDZ_MASK = 0x80, - HDMI_PHY_CONF0_PDZ_OFFSET = 7, -@@ -1097,6 +1135,11 @@ enum { - HDMI_I2CM_CTLINT_NAC_MASK = 0x40, - HDMI_I2CM_CTLINT_ARB_POL = 0x8, - HDMI_I2CM_CTLINT_ARB_MASK = 0x4, -+ -+/* I2CM_DIV field values */ -+ HDMI_I2CM_DIV_FAST_STD_MODE = 0x8, -+ HDMI_I2CM_DIV_FAST_MODE = 0x8, -+ HDMI_I2CM_DIV_STD_MODE = 0, - }; - - /* -diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c -index fe33092abbe7..b3a4ecaa8bf9 100644 ---- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c -+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c -@@ -4,21 +4,31 @@ - */ - - #include -+#include -+#include - #include - #include - #include - #include - #include --#include -+#include - -+#include -+#include -+#include -+#include - #include - #include - #include - #include - #include - -+#include -+ - #include "rockchip_drm_drv.h" - -+#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) -+ - #define RK3228_GRF_SOC_CON2 0x0408 - #define RK3228_HDMI_SDAIN_MSK BIT(14) - #define RK3228_HDMI_SCLIN_MSK BIT(13) -@@ -29,8 +39,11 @@ - - #define RK3288_GRF_SOC_CON6 0x025C - #define RK3288_HDMI_LCDC_SEL BIT(4) --#define RK3328_GRF_SOC_CON2 0x0408 -+#define RK3288_GRF_SOC_CON16 0x03a8 -+#define RK3288_HDMI_LCDC0_YUV420 BIT(2) -+#define RK3288_HDMI_LCDC1_YUV420 BIT(3) - -+#define RK3328_GRF_SOC_CON2 0x0408 - #define RK3328_HDMI_SDAIN_MSK BIT(11) - #define RK3328_HDMI_SCLIN_MSK BIT(10) - #define RK3328_HDMI_HPD_IOE BIT(2) -@@ -54,34 +67,174 @@ - #define RK3568_HDMI_SDAIN_MSK BIT(15) - #define RK3568_HDMI_SCLIN_MSK BIT(14) - --#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) -+#define RK3588_GRF_SOC_CON2 0x0308 -+#define RK3588_HDMI1_HPD_INT_MSK BIT(15) -+#define RK3588_HDMI1_HPD_INT_CLR BIT(14) -+#define RK3588_HDMI0_HPD_INT_MSK BIT(13) -+#define RK3588_HDMI0_HPD_INT_CLR BIT(12) -+#define RK3588_GRF_SOC_CON7 0x031c -+#define RK3588_SET_HPD_PATH_MASK (0x3 << 12) -+#define RK3588_GRF_SOC_STATUS1 0x0384 -+#define RK3588_HDMI0_LOW_MORETHAN100MS BIT(20) -+#define RK3588_HDMI0_HPD_PORT_LEVEL BIT(19) -+#define RK3588_HDMI0_IHPD_PORT BIT(18) -+#define RK3588_HDMI0_OHPD_INT BIT(17) -+#define RK3588_HDMI0_LEVEL_INT BIT(16) -+#define RK3588_HDMI0_INTR_CHANGE_CNT (0x7 << 13) -+#define RK3588_HDMI1_LOW_MORETHAN100MS BIT(28) -+#define RK3588_HDMI1_HPD_PORT_LEVEL BIT(27) -+#define RK3588_HDMI1_IHPD_PORT BIT(26) -+#define RK3588_HDMI1_OHPD_INT BIT(25) -+#define RK3588_HDMI1_LEVEL_INT BIT(24) -+#define RK3588_HDMI1_INTR_CHANGE_CNT (0x7 << 21) -+ -+#define RK3588_GRF_VO1_CON3 0x000c -+#define RK3588_COLOR_FORMAT_MASK 0xf -+#define RK3588_YUV444 0x2 -+#define RK3588_YUV420 0x3 -+#define RK3588_COMPRESSED_DATA 0xb -+#define RK3588_COLOR_DEPTH_MASK (0xf << 4) -+#define RK3588_8BPC (0x5 << 4) -+#define RK3588_10BPC (0x6 << 4) -+#define RK3588_CECIN_MASK BIT(8) -+#define RK3588_SCLIN_MASK BIT(9) -+#define RK3588_SDAIN_MASK BIT(10) -+#define RK3588_MODE_MASK BIT(11) -+#define RK3588_COMPRESS_MODE_MASK BIT(12) -+#define RK3588_I2S_SEL_MASK BIT(13) -+#define RK3588_SPDIF_SEL_MASK BIT(14) -+#define RK3588_GRF_VO1_CON4 0x0010 -+#define RK3588_HDMI21_MASK BIT(0) -+#define RK3588_GRF_VO1_CON9 0x0024 -+#define RK3588_HDMI0_GRANT_SEL BIT(10) -+#define RK3588_HDMI0_GRANT_SW BIT(11) -+#define RK3588_HDMI1_GRANT_SEL BIT(12) -+#define RK3588_HDMI1_GRANT_SW BIT(13) -+#define RK3588_GRF_VO1_CON6 0x0018 -+#define RK3588_GRF_VO1_CON7 0x001c -+ -+#define COLOR_DEPTH_10BIT BIT(31) -+#define HDMI_FRL_MODE BIT(30) -+#define HDMI_EARC_MODE BIT(29) -+ -+#define HDMI20_MAX_RATE 600000 -+#define HDMI_8K60_RATE 2376000 - - /** - * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips - * @lcdsel_grf_reg: grf register offset of lcdc select -+ * @ddc_en_reg: grf register offset of hdmi ddc enable - * @lcdsel_big: reg value of selecting vop big for HDMI - * @lcdsel_lit: reg value of selecting vop little for HDMI - */ - struct rockchip_hdmi_chip_data { - int lcdsel_grf_reg; -+ int ddc_en_reg; - u32 lcdsel_big; - u32 lcdsel_lit; -+ bool split_mode; -+}; -+ -+enum hdmi_frl_rate_per_lane { -+ FRL_12G_PER_LANE = 12, -+ FRL_10G_PER_LANE = 10, -+ FRL_8G_PER_LANE = 8, -+ FRL_6G_PER_LANE = 6, -+ FRL_3G_PER_LANE = 3, -+}; -+ -+enum rk_if_color_depth { -+ RK_IF_DEPTH_8, -+ RK_IF_DEPTH_10, -+ RK_IF_DEPTH_12, -+ RK_IF_DEPTH_16, -+ RK_IF_DEPTH_420_10, -+ RK_IF_DEPTH_420_12, -+ RK_IF_DEPTH_420_16, -+ RK_IF_DEPTH_6, -+ RK_IF_DEPTH_MAX, -+}; -+ -+enum rk_if_color_format { -+ RK_IF_FORMAT_RGB, /* default RGB */ -+ RK_IF_FORMAT_YCBCR444, /* YCBCR 444 */ -+ RK_IF_FORMAT_YCBCR422, /* YCBCR 422 */ -+ RK_IF_FORMAT_YCBCR420, /* YCBCR 420 */ -+ RK_IF_FORMAT_YCBCR_HQ, /* Highest subsampled YUV */ -+ RK_IF_FORMAT_YCBCR_LQ, /* Lowest subsampled YUV */ -+ RK_IF_FORMAT_MAX, - }; - - struct rockchip_hdmi { - struct device *dev; - struct regmap *regmap; -+ struct regmap *vo1_regmap; - struct rockchip_encoder encoder; -+ struct drm_device *drm_dev; - const struct rockchip_hdmi_chip_data *chip_data; -- const struct dw_hdmi_plat_data *plat_data; -- struct clk *ref_clk; -+ struct dw_hdmi_plat_data *plat_data; -+ struct clk *aud_clk; -+ struct clk *phyref_clk; - struct clk *grf_clk; -+ struct clk *hclk_vio; -+ struct clk *hclk_vo1; -+ struct clk *hclk_vop; -+ struct clk *hpd_clk; -+ struct clk *pclk; -+ struct clk *earc_clk; -+ struct clk *hdmitx_ref; - struct dw_hdmi *hdmi; -- struct regulator *avdd_0v9; -- struct regulator *avdd_1v8; -+ struct dw_hdmi_qp *hdmi_qp; -+ - struct phy *phy; -+ -+ u32 max_tmdsclk; -+ bool unsupported_yuv_input; -+ bool unsupported_deep_color; -+ bool skip_check_420_mode; -+ u8 force_output; -+ u8 id; -+ bool hpd_stat; -+ bool is_hdmi_qp; -+ bool user_split_mode; -+ -+ unsigned long bus_format; -+ unsigned long output_bus_format; -+ unsigned long enc_out_encoding; -+ int color_changed; -+ int hpd_irq; -+ int vp_id; -+ -+ struct drm_property *color_depth_property; -+ struct drm_property *hdmi_output_property; -+ struct drm_property *colordepth_capacity; -+ struct drm_property *outputmode_capacity; -+ struct drm_property *quant_range; -+ struct drm_property *hdr_panel_metadata_property; -+ struct drm_property *next_hdr_sink_data_property; -+ struct drm_property *output_hdmi_dvi; -+ struct drm_property *output_type_capacity; -+ struct drm_property *user_split_mode_prop; -+ -+ struct drm_property_blob *hdr_panel_blob_ptr; -+ struct drm_property_blob *next_hdr_data_ptr; -+ -+ unsigned int colordepth; -+ unsigned int colorimetry; -+ unsigned int hdmi_quant_range; -+ unsigned int phy_bus_width; -+ enum rk_if_color_format hdmi_output; -+ -+ u8 max_frl_rate_per_lane; -+ u8 max_lanes; -+ struct dw_hdmi_link_config link_cfg; -+ struct gpio_desc *enable_gpio; -+ -+ struct delayed_work work; -+ struct workqueue_struct *workqueue; - }; - -+ - static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder) - { - struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); -@@ -89,88 +242,195 @@ static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder) - return container_of(rkencoder, struct rockchip_hdmi, encoder); - } - -+/* -+ * There are some rates that would be ranged for better clock jitter at -+ * Chrome OS tree, like 25.175Mhz would range to 25.170732Mhz. But due -+ * to the clock is aglined to KHz in struct drm_display_mode, this would -+ * bring some inaccurate error if we still run the compute_n math, so -+ * let's just code an const table for it until we can actually get the -+ * right clock rate. -+ */ -+static const struct dw_hdmi_audio_tmds_n rockchip_werid_tmds_n_table[] = { -+ /* 25176471 for 25.175 MHz = 428000000 / 17. */ -+ { .tmds = 25177000, .n_32k = 4352, .n_44k1 = 14994, .n_48k = 6528, }, -+ /* 57290323 for 57.284 MHz */ -+ { .tmds = 57291000, .n_32k = 3968, .n_44k1 = 4557, .n_48k = 5952, }, -+ /* 74437500 for 74.44 MHz = 297750000 / 4 */ -+ { .tmds = 74438000, .n_32k = 8192, .n_44k1 = 18816, .n_48k = 4096, }, -+ /* 118666667 for 118.68 MHz */ -+ { .tmds = 118667000, .n_32k = 4224, .n_44k1 = 5292, .n_48k = 6336, }, -+ /* 121714286 for 121.75 MHz */ -+ { .tmds = 121715000, .n_32k = 4480, .n_44k1 = 6174, .n_48k = 6272, }, -+ /* 136800000 for 136.75 MHz */ -+ { .tmds = 136800000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, -+ /* End of table */ -+ { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, }, -+}; -+ - static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = { - { -- 27000000, { -- { 0x00b3, 0x0000}, -- { 0x2153, 0x0000}, -- { 0x40f3, 0x0000} -- }, -- }, { -- 36000000, { -- { 0x00b3, 0x0000}, -- { 0x2153, 0x0000}, -- { 0x40f3, 0x0000} -- }, -- }, { -- 40000000, { -- { 0x00b3, 0x0000}, -- { 0x2153, 0x0000}, -- { 0x40f3, 0x0000} -- }, -- }, { -- 54000000, { -- { 0x0072, 0x0001}, -- { 0x2142, 0x0001}, -- { 0x40a2, 0x0001}, -- }, -- }, { -- 65000000, { -- { 0x0072, 0x0001}, -- { 0x2142, 0x0001}, -- { 0x40a2, 0x0001}, -- }, -- }, { -- 66000000, { -- { 0x013e, 0x0003}, -- { 0x217e, 0x0002}, -- { 0x4061, 0x0002} -- }, -- }, { -- 74250000, { -- { 0x0072, 0x0001}, -- { 0x2145, 0x0002}, -- { 0x4061, 0x0002} -- }, -- }, { -- 83500000, { -- { 0x0072, 0x0001}, -- }, -- }, { -- 108000000, { -- { 0x0051, 0x0002}, -- { 0x2145, 0x0002}, -- { 0x4061, 0x0002} -- }, -- }, { -- 106500000, { -- { 0x0051, 0x0002}, -- { 0x2145, 0x0002}, -- { 0x4061, 0x0002} -- }, -- }, { -- 146250000, { -- { 0x0051, 0x0002}, -- { 0x2145, 0x0002}, -- { 0x4061, 0x0002} -- }, -- }, { -- 148500000, { -- { 0x0051, 0x0003}, -- { 0x214c, 0x0003}, -- { 0x4064, 0x0003} -- }, -- }, { -+ 30666000, { -+ { 0x00b3, 0x0000 }, -+ { 0x2153, 0x0000 }, -+ { 0x40f3, 0x0000 }, -+ }, -+ }, { -+ 36800000, { -+ { 0x00b3, 0x0000 }, -+ { 0x2153, 0x0000 }, -+ { 0x40a2, 0x0001 }, -+ }, -+ }, { -+ 46000000, { -+ { 0x00b3, 0x0000 }, -+ { 0x2142, 0x0001 }, -+ { 0x40a2, 0x0001 }, -+ }, -+ }, { -+ 61333000, { -+ { 0x0072, 0x0001 }, -+ { 0x2142, 0x0001 }, -+ { 0x40a2, 0x0001 }, -+ }, -+ }, { -+ 73600000, { -+ { 0x0072, 0x0001 }, -+ { 0x2142, 0x0001 }, -+ { 0x4061, 0x0002 }, -+ }, -+ }, { -+ 92000000, { -+ { 0x0072, 0x0001 }, -+ { 0x2145, 0x0002 }, -+ { 0x4061, 0x0002 }, -+ }, -+ }, { -+ 122666000, { -+ { 0x0051, 0x0002 }, -+ { 0x2145, 0x0002 }, -+ { 0x4061, 0x0002 }, -+ }, -+ }, { -+ 147200000, { -+ { 0x0051, 0x0002 }, -+ { 0x2145, 0x0002 }, -+ { 0x4064, 0x0003 }, -+ }, -+ }, { -+ 184000000, { -+ { 0x0051, 0x0002 }, -+ { 0x214c, 0x0003 }, -+ { 0x4064, 0x0003 }, -+ }, -+ }, { -+ 226666000, { -+ { 0x0040, 0x0003 }, -+ { 0x214c, 0x0003 }, -+ { 0x4064, 0x0003 }, -+ }, -+ }, { -+ 272000000, { -+ { 0x0040, 0x0003 }, -+ { 0x214c, 0x0003 }, -+ { 0x5a64, 0x0003 }, -+ }, -+ }, { - 340000000, { - { 0x0040, 0x0003 }, - { 0x3b4c, 0x0003 }, - { 0x5a64, 0x0003 }, - }, -- }, { -+ }, { -+ 600000000, { -+ { 0x1a40, 0x0003 }, -+ { 0x3b4c, 0x0003 }, -+ { 0x5a64, 0x0003 }, -+ }, -+ }, { -+ ~0UL, { -+ { 0x0000, 0x0000 }, -+ { 0x0000, 0x0000 }, -+ { 0x0000, 0x0000 }, -+ }, -+ } -+}; -+ -+static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_420[] = { -+ { -+ 30666000, { -+ { 0x00b7, 0x0000 }, -+ { 0x2157, 0x0000 }, -+ { 0x40f7, 0x0000 }, -+ }, -+ }, { -+ 92000000, { -+ { 0x00b7, 0x0000 }, -+ { 0x2143, 0x0001 }, -+ { 0x40a3, 0x0001 }, -+ }, -+ }, { -+ 184000000, { -+ { 0x0073, 0x0001 }, -+ { 0x2146, 0x0002 }, -+ { 0x4062, 0x0002 }, -+ }, -+ }, { -+ 340000000, { -+ { 0x0052, 0x0003 }, -+ { 0x214d, 0x0003 }, -+ { 0x4065, 0x0003 }, -+ }, -+ }, { -+ 600000000, { -+ { 0x0041, 0x0003 }, -+ { 0x3b4d, 0x0003 }, -+ { 0x5a65, 0x0003 }, -+ }, -+ }, { -+ ~0UL, { -+ { 0x0000, 0x0000 }, -+ { 0x0000, 0x0000 }, -+ { 0x0000, 0x0000 }, -+ }, -+ } -+}; -+ -+static const struct dw_hdmi_mpll_config rockchip_rk3288w_mpll_cfg_420[] = { -+ { -+ 30666000, { -+ { 0x00b7, 0x0000 }, -+ { 0x2157, 0x0000 }, -+ { 0x40f7, 0x0000 }, -+ }, -+ }, { -+ 92000000, { -+ { 0x00b7, 0x0000 }, -+ { 0x2143, 0x0001 }, -+ { 0x40a3, 0x0001 }, -+ }, -+ }, { -+ 184000000, { -+ { 0x0073, 0x0001 }, -+ { 0x2146, 0x0002 }, -+ { 0x4062, 0x0002 }, -+ }, -+ }, { -+ 340000000, { -+ { 0x0052, 0x0003 }, -+ { 0x214d, 0x0003 }, -+ { 0x4065, 0x0003 }, -+ }, -+ }, { -+ 600000000, { -+ { 0x0040, 0x0003 }, -+ { 0x3b4c, 0x0003 }, -+ { 0x5a65, 0x0003 }, -+ }, -+ }, { - ~0UL, { -- { 0x00a0, 0x000a }, -- { 0x2001, 0x000f }, -- { 0x4002, 0x000f }, -+ { 0x0000, 0x0000 }, -+ { 0x0000, 0x0000 }, -+ { 0x0000, 0x0000 }, - }, - } - }; -@@ -178,198 +438,2088 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = { - static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = { - /* pixelclk bpp8 bpp10 bpp12 */ - { -- 40000000, { 0x0018, 0x0018, 0x0018 }, -- }, { -- 65000000, { 0x0028, 0x0028, 0x0028 }, -- }, { -- 66000000, { 0x0038, 0x0038, 0x0038 }, -- }, { -- 74250000, { 0x0028, 0x0038, 0x0038 }, -- }, { -- 83500000, { 0x0028, 0x0038, 0x0038 }, -- }, { -- 146250000, { 0x0038, 0x0038, 0x0038 }, -- }, { -- 148500000, { 0x0000, 0x0038, 0x0038 }, -- }, { - 600000000, { 0x0000, 0x0000, 0x0000 }, -- }, { -+ }, { - ~0UL, { 0x0000, 0x0000, 0x0000}, - } - }; - --static const struct dw_hdmi_phy_config rockchip_phy_config[] = { -+static struct dw_hdmi_phy_config rockchip_phy_config[] = { - /*pixelclk symbol term vlev*/ - { 74250000, 0x8009, 0x0004, 0x0272}, -- { 148500000, 0x802b, 0x0004, 0x028d}, -+ { 165000000, 0x802b, 0x0004, 0x0209}, - { 297000000, 0x8039, 0x0005, 0x028d}, -- { ~0UL, 0x0000, 0x0000, 0x0000} -+ { 594000000, 0x8039, 0x0000, 0x019d}, -+ { ~0UL, 0x0000, 0x0000, 0x0000}, -+ { ~0UL, 0x0000, 0x0000, 0x0000}, - }; - --static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) -+enum ROW_INDEX_BPP { -+ ROW_INDEX_6BPP = 0, -+ ROW_INDEX_8BPP, -+ ROW_INDEX_10BPP, -+ ROW_INDEX_12BPP, -+ ROW_INDEX_23BPP, -+ MAX_ROW_INDEX -+}; -+ -+enum COLUMN_INDEX_BPC { -+ COLUMN_INDEX_8BPC = 0, -+ COLUMN_INDEX_10BPC, -+ COLUMN_INDEX_12BPC, -+ COLUMN_INDEX_14BPC, -+ COLUMN_INDEX_16BPC, -+ MAX_COLUMN_INDEX -+}; -+ -+#define PPS_TABLE_LEN 8 -+#define PPS_BPP_LEN 4 -+#define PPS_BPC_LEN 2 -+ -+struct pps_data { -+ u32 pic_width; -+ u32 pic_height; -+ u32 slice_width; -+ u32 slice_height; -+ bool convert_rgb; -+ u8 bpc; -+ u8 bpp; -+ u8 raw_pps[128]; -+}; -+ -+/* -+ * Selected Rate Control Related Parameter Recommended Values -+ * from DSC_v1.11 spec & C Model release: DSC_model_20161212 -+ */ -+static struct pps_data pps_datas[PPS_TABLE_LEN] = { -+ { -+ /* 7680x4320/960X96 rgb 8bpc 12bpp */ -+ 7680, 4320, 960, 96, 1, 8, 192, -+ { -+ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xc0, 0x10, 0xe0, -+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0, -+ 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9, -+ 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa, -+ 0x08, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00, -+ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, -+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, -+ 0x7d, 0x7e, 0x00, 0x82, 0x00, 0xc0, 0x09, 0x00, -+ 0x09, 0x7e, 0x19, 0xbc, 0x19, 0xba, 0x19, 0xf8, -+ 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76, -+ 0x2a, 0x76, 0x2a, 0x74, 0x3a, 0xb4, 0x52, 0xf4, -+ 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 -+ }, -+ }, -+ { -+ /* 7680x4320/960X96 rgb 8bpc 11bpp */ -+ 7680, 4320, 960, 96, 1, 8, 176, -+ { -+ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xb0, 0x10, 0xe0, -+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28, -+ 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0, -+ 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33, -+ 0x0f, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00, -+ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, -+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, -+ 0x7d, 0x7e, 0x00, 0x82, 0x01, 0x00, 0x09, 0x40, -+ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, -+ 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76, -+ 0x2a, 0x76, 0x2a, 0xb4, 0x3a, 0xb4, 0x52, 0xf4, -+ 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 -+ }, -+ }, -+ { -+ /* 7680x4320/960X96 rgb 8bpc 10bpp */ -+ 7680, 4320, 960, 96, 1, 8, 160, -+ { -+ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xa0, 0x10, 0xe0, -+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0, -+ 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0, -+ 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb, -+ 0x16, 0x00, 0x10, 0xec, 0x03, 0x0c, 0x20, 0x00, -+ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, -+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, -+ 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40, -+ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, -+ 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6, -+ 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x5b, 0x34, -+ 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 -+ }, -+ }, -+ { -+ /* 7680x4320/960X96 rgb 8bpc 9bpp */ -+ 7680, 4320, 960, 96, 1, 8, 144, -+ { -+ 0x12, 0x00, 0x00, 0x8d, 0x30, 0x90, 0x10, 0xe0, -+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38, -+ 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7, -+ 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa, -+ 0x17, 0x00, 0x10, 0xf1, 0x03, 0x0c, 0x20, 0x00, -+ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, -+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, -+ 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40, -+ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, -+ 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6, -+ 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x63, 0x74, -+ 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 -+ }, -+ }, -+ { -+ /* 7680x4320/960X96 rgb 10bpc 12bpp */ -+ 7680, 4320, 960, 96, 1, 10, 192, -+ { -+ 0x12, 0x00, 0x00, 0xad, 0x30, 0xc0, 0x10, 0xe0, -+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0, -+ 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9, -+ 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa, -+ 0x08, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00, -+ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, -+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, -+ 0x7d, 0x7e, 0x01, 0x02, 0x11, 0x80, 0x22, 0x00, -+ 0x22, 0x7e, 0x32, 0xbc, 0x32, 0xba, 0x3a, 0xf8, -+ 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76, -+ 0x4b, 0x76, 0x4b, 0x74, 0x5b, 0xb4, 0x73, 0xf4, -+ 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 -+ }, -+ }, -+ { -+ /* 7680x4320/960X96 rgb 10bpc 11bpp */ -+ 7680, 4320, 960, 96, 1, 10, 176, -+ { -+ 0x12, 0x00, 0x00, 0xad, 0x30, 0xb0, 0x10, 0xe0, -+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28, -+ 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0, -+ 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33, -+ 0x0f, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00, -+ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, -+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, -+ 0x7d, 0x7e, 0x01, 0x42, 0x19, 0xc0, 0x2a, 0x40, -+ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, -+ 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76, -+ 0x4b, 0x76, 0x4b, 0xb4, 0x5b, 0xb4, 0x73, 0xf4, -+ 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 -+ }, -+ }, -+ { -+ /* 7680x4320/960X96 rgb 10bpc 10bpp */ -+ 7680, 4320, 960, 96, 1, 10, 160, -+ { -+ 0x12, 0x00, 0x00, 0xad, 0x30, 0xa0, 0x10, 0xe0, -+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0, -+ 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0, -+ 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb, -+ 0x16, 0x00, 0x10, 0xec, 0x07, 0x10, 0x20, 0x00, -+ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, -+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, -+ 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40, -+ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, -+ 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6, -+ 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x7c, 0x34, -+ 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 -+ }, -+ }, -+ { -+ /* 7680x4320/960X96 rgb 10bpc 9bpp */ -+ 7680, 4320, 960, 96, 1, 10, 144, -+ { -+ 0x12, 0x00, 0x00, 0xad, 0x30, 0x90, 0x10, 0xe0, -+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38, -+ 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7, -+ 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa, -+ 0x17, 0x00, 0x10, 0xf1, 0x07, 0x10, 0x20, 0x00, -+ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, -+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, -+ 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40, -+ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, -+ 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6, -+ 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x84, 0x74, -+ 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 -+ }, -+ }, -+}; -+ -+static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) - { -- struct device_node *np = hdmi->dev->of_node; -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ case MEDIA_BUS_FMT_RGB101010_1X30: -+ case MEDIA_BUS_FMT_RGB121212_1X36: -+ case MEDIA_BUS_FMT_RGB161616_1X48: -+ return true; -+ -+ default: -+ return false; -+ } -+} - -- hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); -- if (IS_ERR(hdmi->regmap)) { -- DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n"); -- return PTR_ERR(hdmi->regmap); -+static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) -+{ -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_YUV8_1X24: -+ case MEDIA_BUS_FMT_YUV10_1X30: -+ case MEDIA_BUS_FMT_YUV12_1X36: -+ case MEDIA_BUS_FMT_YUV16_1X48: -+ return true; -+ -+ default: -+ return false; -+ } -+} -+ -+static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) -+{ -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_UYVY10_1X20: -+ case MEDIA_BUS_FMT_UYVY12_1X24: -+ return true; -+ -+ default: -+ return false; -+ } -+} -+ -+static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) -+{ -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: -+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: -+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: -+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: -+ return true; -+ -+ default: -+ return false; -+ } -+} -+ -+static int hdmi_bus_fmt_color_depth(unsigned int bus_format) -+{ -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ case MEDIA_BUS_FMT_YUV8_1X24: -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: -+ return 8; -+ -+ case MEDIA_BUS_FMT_RGB101010_1X30: -+ case MEDIA_BUS_FMT_YUV10_1X30: -+ case MEDIA_BUS_FMT_UYVY10_1X20: -+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: -+ return 10; -+ -+ case MEDIA_BUS_FMT_RGB121212_1X36: -+ case MEDIA_BUS_FMT_YUV12_1X36: -+ case MEDIA_BUS_FMT_UYVY12_1X24: -+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: -+ return 12; -+ -+ case MEDIA_BUS_FMT_RGB161616_1X48: -+ case MEDIA_BUS_FMT_YUV16_1X48: -+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: -+ return 16; -+ -+ default: -+ return 0; -+ } -+} -+ -+static unsigned int -+hdmi_get_tmdsclock(struct rockchip_hdmi *hdmi, unsigned long pixelclock) -+{ -+ unsigned int tmdsclock = pixelclock; -+ unsigned int depth = -+ hdmi_bus_fmt_color_depth(hdmi->output_bus_format); -+ -+ if (!hdmi_bus_fmt_is_yuv422(hdmi->output_bus_format)) { -+ switch (depth) { -+ case 16: -+ tmdsclock = pixelclock * 2; -+ break; -+ case 12: -+ tmdsclock = pixelclock * 3 / 2; -+ break; -+ case 10: -+ tmdsclock = pixelclock * 5 / 4; -+ break; -+ default: -+ break; -+ } -+ } -+ -+ return tmdsclock; -+} -+ -+static int rockchip_hdmi_match_by_id(struct device *dev, const void *data) -+{ -+ struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); -+ const unsigned int *id = data; -+ -+ return hdmi->id == *id; -+} -+ -+static struct rockchip_hdmi * -+rockchip_hdmi_find_by_id(struct device_driver *drv, unsigned int id) -+{ -+ struct device *dev; -+ -+ dev = driver_find_device(drv, NULL, &id, rockchip_hdmi_match_by_id); -+ if (!dev) -+ return NULL; -+ -+ return dev_get_drvdata(dev); -+} -+ -+static void hdmi_select_link_config(struct rockchip_hdmi *hdmi, -+ struct drm_crtc_state *crtc_state, -+ unsigned int tmdsclk) -+{ -+ struct drm_display_mode mode; -+ int max_lanes, max_rate_per_lane; -+ int max_dsc_lanes, max_dsc_rate_per_lane; -+ unsigned long max_frl_rate; -+ -+ drm_mode_copy(&mode, &crtc_state->mode); -+ -+ max_lanes = hdmi->max_lanes; -+ max_rate_per_lane = hdmi->max_frl_rate_per_lane; -+ max_frl_rate = max_lanes * max_rate_per_lane * 1000000; -+ -+ hdmi->link_cfg.dsc_mode = false; -+ hdmi->link_cfg.frl_lanes = max_lanes; -+ hdmi->link_cfg.rate_per_lane = max_rate_per_lane; -+ -+ if (!max_frl_rate || (tmdsclk < HDMI20_MAX_RATE && mode.clock < HDMI20_MAX_RATE)) { -+ dev_dbg(hdmi->dev, "use tmds mode\n"); -+ hdmi->link_cfg.frl_mode = false; -+ return; -+ } -+#if 0 -+ hdmi->link_cfg.frl_mode = true; -+ -+ if (!hdmi->dsc_cap.v_1p2) -+ return; -+ -+ max_dsc_lanes = hdmi->dsc_cap.max_lanes; -+ max_dsc_rate_per_lane = -+ hdmi->dsc_cap.max_frl_rate_per_lane; -+ -+ if (mode.clock >= HDMI_8K60_RATE && -+ !hdmi_bus_fmt_is_yuv420(hdmi->bus_format) && -+ !hdmi_bus_fmt_is_yuv422(hdmi->bus_format)) { -+ hdmi->link_cfg.dsc_mode = true; -+ hdmi->link_cfg.frl_lanes = max_dsc_lanes; -+ hdmi->link_cfg.rate_per_lane = max_dsc_rate_per_lane; -+ } else { -+ hdmi->link_cfg.dsc_mode = false; -+ hdmi->link_cfg.frl_lanes = max_lanes; -+ hdmi->link_cfg.rate_per_lane = max_rate_per_lane; -+ } -+#endif -+} -+ -+#if 0 -+///////////////////////////////////////////////////////////////////////////////////// -+ -+static int hdmi_dsc_get_slice_height(int vactive) -+{ -+ int slice_height; -+ -+ /* -+ * Slice Height determination : HDMI2.1 Section 7.7.5.2 -+ * Select smallest slice height >=96, that results in a valid PPS and -+ * requires minimum padding lines required for final slice. -+ * -+ * Assumption : Vactive is even. -+ */ -+ for (slice_height = 96; slice_height <= vactive; slice_height += 2) -+ if (vactive % slice_height == 0) -+ return slice_height; -+ -+ return 0; -+} -+ -+static int hdmi_dsc_get_num_slices(struct rockchip_hdmi *hdmi, -+ struct drm_crtc_state *crtc_state, -+ int src_max_slices, int src_max_slice_width, -+ int hdmi_max_slices, int hdmi_throughput) -+{ -+/* Pixel rates in KPixels/sec */ -+#define HDMI_DSC_PEAK_PIXEL_RATE 2720000 -+/* -+ * Rates at which the source and sink are required to process pixels in each -+ * slice, can be two levels: either at least 340000KHz or at least 40000KHz. -+ */ -+#define HDMI_DSC_MAX_ENC_THROUGHPUT_0 340000 -+#define HDMI_DSC_MAX_ENC_THROUGHPUT_1 400000 -+ -+/* Spec limits the slice width to 2720 pixels */ -+#define MAX_HDMI_SLICE_WIDTH 2720 -+ int kslice_adjust; -+ int adjusted_clk_khz; -+ int min_slices; -+ int target_slices; -+ int max_throughput; /* max clock freq. in khz per slice */ -+ int max_slice_width; -+ int slice_width; -+ int pixel_clock = crtc_state->mode.clock; -+ -+ if (!hdmi_throughput) -+ return 0; -+ -+ /* -+ * Slice Width determination : HDMI2.1 Section 7.7.5.1 -+ * kslice_adjust factor for 4:2:0, and 4:2:2 formats is 0.5, where as -+ * for 4:4:4 is 1.0. Multiplying these factors by 10 and later -+ * dividing adjusted clock value by 10. -+ */ -+ if (hdmi_bus_fmt_is_yuv444(hdmi->output_bus_format) || -+ hdmi_bus_fmt_is_rgb(hdmi->output_bus_format)) -+ kslice_adjust = 10; -+ else -+ kslice_adjust = 5; -+ -+ /* -+ * As per spec, the rate at which the source and the sink process -+ * the pixels per slice are at two levels: at least 340Mhz or 400Mhz. -+ * This depends upon the pixel clock rate and output formats -+ * (kslice adjust). -+ * If pixel clock * kslice adjust >= 2720MHz slices can be processed -+ * at max 340MHz, otherwise they can be processed at max 400MHz. -+ */ -+ -+ adjusted_clk_khz = DIV_ROUND_UP(kslice_adjust * pixel_clock, 10); -+ -+ if (adjusted_clk_khz <= HDMI_DSC_PEAK_PIXEL_RATE) -+ max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_0; -+ else -+ max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_1; -+ -+ /* -+ * Taking into account the sink's capability for maximum -+ * clock per slice (in MHz) as read from HF-VSDB. -+ */ -+ max_throughput = min(max_throughput, hdmi_throughput * 1000); -+ -+ min_slices = DIV_ROUND_UP(adjusted_clk_khz, max_throughput); -+ max_slice_width = min(MAX_HDMI_SLICE_WIDTH, src_max_slice_width); -+ -+ /* -+ * Keep on increasing the num of slices/line, starting from min_slices -+ * per line till we get such a number, for which the slice_width is -+ * just less than max_slice_width. The slices/line selected should be -+ * less than or equal to the max horizontal slices that the combination -+ * of PCON encoder and HDMI decoder can support. -+ */ -+ do { -+ if (min_slices <= 1 && src_max_slices >= 1 && hdmi_max_slices >= 1) -+ target_slices = 1; -+ else if (min_slices <= 2 && src_max_slices >= 2 && hdmi_max_slices >= 2) -+ target_slices = 2; -+ else if (min_slices <= 4 && src_max_slices >= 4 && hdmi_max_slices >= 4) -+ target_slices = 4; -+ else if (min_slices <= 8 && src_max_slices >= 8 && hdmi_max_slices >= 8) -+ target_slices = 8; -+ else if (min_slices <= 12 && src_max_slices >= 12 && hdmi_max_slices >= 12) -+ target_slices = 12; -+ else if (min_slices <= 16 && src_max_slices >= 16 && hdmi_max_slices >= 16) -+ target_slices = 16; -+ else -+ return 0; -+ -+ slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, target_slices); -+ if (slice_width > max_slice_width) -+ min_slices = target_slices + 1; -+ } while (slice_width > max_slice_width); -+ -+ return target_slices; -+} -+ -+static int hdmi_dsc_slices(struct rockchip_hdmi *hdmi, -+ struct drm_crtc_state *crtc_state) -+{ -+ int hdmi_throughput = hdmi->dsc_cap.clk_per_slice; -+ int hdmi_max_slices = hdmi->dsc_cap.max_slices; -+ int rk_max_slices = 8; -+ int rk_max_slice_width = 2048; -+ -+ return hdmi_dsc_get_num_slices(hdmi, crtc_state, rk_max_slices, -+ rk_max_slice_width, -+ hdmi_max_slices, hdmi_throughput); -+} -+ -+static int -+hdmi_dsc_get_bpp(struct rockchip_hdmi *hdmi, int src_fractional_bpp, -+ int slice_width, int num_slices, bool hdmi_all_bpp, -+ int hdmi_max_chunk_bytes) -+{ -+ int max_dsc_bpp, min_dsc_bpp; -+ int target_bytes; -+ bool bpp_found = false; -+ int bpp_decrement_x16; -+ int bpp_target; -+ int bpp_target_x16; -+ -+ /* -+ * Get min bpp and max bpp as per Table 7.23, in HDMI2.1 spec -+ * Start with the max bpp and keep on decrementing with -+ * fractional bpp, if supported by PCON DSC encoder -+ * -+ * for each bpp we check if no of bytes can be supported by HDMI sink -+ */ -+ -+ /* only 9\10\12 bpp was tested */ -+ min_dsc_bpp = 9; -+ max_dsc_bpp = 12; -+ -+ /* -+ * Taking into account if all dsc_all_bpp supported by HDMI2.1 sink -+ * Section 7.7.34 : Source shall not enable compressed Video -+ * Transport with bpp_target settings above 12 bpp unless -+ * DSC_all_bpp is set to 1. -+ */ -+ if (!hdmi_all_bpp) -+ max_dsc_bpp = min(max_dsc_bpp, 12); -+ -+ /* -+ * The Sink has a limit of compressed data in bytes for a scanline, -+ * as described in max_chunk_bytes field in HFVSDB block of edid. -+ * The no. of bytes depend on the target bits per pixel that the -+ * source configures. So we start with the max_bpp and calculate -+ * the target_chunk_bytes. We keep on decrementing the target_bpp, -+ * till we get the target_chunk_bytes just less than what the sink's -+ * max_chunk_bytes, or else till we reach the min_dsc_bpp. -+ * -+ * The decrement is according to the fractional support from PCON DSC -+ * encoder. For fractional BPP we use bpp_target as a multiple of 16. -+ * -+ * bpp_target_x16 = bpp_target * 16 -+ * So we need to decrement by {1, 2, 4, 8, 16} for fractional bpps -+ * {1/16, 1/8, 1/4, 1/2, 1} respectively. -+ */ -+ -+ bpp_target = max_dsc_bpp; -+ -+ /* src does not support fractional bpp implies decrement by 16 for bppx16 */ -+ if (!src_fractional_bpp) -+ src_fractional_bpp = 1; -+ bpp_decrement_x16 = DIV_ROUND_UP(16, src_fractional_bpp); -+ bpp_target_x16 = bpp_target * 16; -+ -+ while (bpp_target_x16 > (min_dsc_bpp * 16)) { -+ int bpp; -+ -+ bpp = DIV_ROUND_UP(bpp_target_x16, 16); -+ target_bytes = DIV_ROUND_UP((num_slices * slice_width * bpp), 8); -+ if (target_bytes <= hdmi_max_chunk_bytes) { -+ bpp_found = true; -+ break; -+ } -+ bpp_target_x16 -= bpp_decrement_x16; -+ } -+ if (bpp_found) -+ return bpp_target_x16; -+ -+ return 0; -+} -+ -+static int -+dw_hdmi_dsc_bpp(struct rockchip_hdmi *hdmi, -+ int num_slices, int slice_width) -+{ -+ bool hdmi_all_bpp = hdmi->dsc_cap.all_bpp; -+ int fractional_bpp = 0; -+ int hdmi_max_chunk_bytes = hdmi->dsc_cap.total_chunk_kbytes * 1024; -+ -+ return hdmi_dsc_get_bpp(hdmi, fractional_bpp, slice_width, -+ num_slices, hdmi_all_bpp, -+ hdmi_max_chunk_bytes); -+} -+ -+static int dw_hdmi_qp_set_link_cfg(struct rockchip_hdmi *hdmi, -+ u16 pic_width, u16 pic_height, -+ u16 slice_width, u16 slice_height, -+ u16 bits_per_pixel, u8 bits_per_component) -+{ -+ int i; -+ -+ for (i = 0; i < PPS_TABLE_LEN; i++) -+ if (pic_width == pps_datas[i].pic_width && -+ pic_height == pps_datas[i].pic_height && -+ slice_width == pps_datas[i].slice_width && -+ slice_height == pps_datas[i].slice_height && -+ bits_per_component == pps_datas[i].bpc && -+ bits_per_pixel == pps_datas[i].bpp && -+ hdmi_bus_fmt_is_rgb(hdmi->output_bus_format) == pps_datas[i].convert_rgb) -+ break; -+ -+ if (i == PPS_TABLE_LEN) { -+ dev_err(hdmi->dev, "can't find pps cfg!\n"); -+ return -EINVAL; -+ } -+ -+ memcpy(hdmi->link_cfg.pps_payload, pps_datas[i].raw_pps, 128); -+ hdmi->link_cfg.hcactive = DIV_ROUND_UP(slice_width * (bits_per_pixel / 16), 8) * -+ (pic_width / slice_width); -+ -+ return 0; -+} -+ -+static void dw_hdmi_qp_dsc_configure(struct rockchip_hdmi *hdmi, -+ struct rockchip_crtc_state *s, -+ struct drm_crtc_state *crtc_state) -+{ -+ int ret; -+ int slice_height; -+ int slice_width; -+ int bits_per_pixel; -+ int slice_count; -+ bool hdmi_is_dsc_1_2; -+ unsigned int depth = hdmi_bus_fmt_color_depth(hdmi->output_bus_format); -+ -+ if (!crtc_state) -+ return; -+ -+ hdmi_is_dsc_1_2 = hdmi->dsc_cap.v_1p2; -+ -+ if (!hdmi_is_dsc_1_2) -+ return; -+ -+ slice_height = hdmi_dsc_get_slice_height(crtc_state->mode.vdisplay); -+ if (!slice_height) -+ return; -+ -+ slice_count = hdmi_dsc_slices(hdmi, crtc_state); -+ if (!slice_count) -+ return; -+ -+ slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, slice_count); -+ -+ bits_per_pixel = dw_hdmi_dsc_bpp(hdmi, slice_count, slice_width); -+ if (!bits_per_pixel) -+ return; -+ -+ ret = dw_hdmi_qp_set_link_cfg(hdmi, crtc_state->mode.hdisplay, -+ crtc_state->mode.vdisplay, slice_width, -+ slice_height, bits_per_pixel, depth); -+ -+ if (ret) { -+ dev_err(hdmi->dev, "set vdsc cfg failed\n"); -+ return; -+ } -+ dev_dbg(hdmi->dev, "dsc_enable\n"); -+ s->dsc_enable = 1; -+ s->dsc_sink_cap.version_major = 1; -+ s->dsc_sink_cap.version_minor = 2; -+ s->dsc_sink_cap.slice_width = slice_width; -+ s->dsc_sink_cap.slice_height = slice_height; -+ s->dsc_sink_cap.target_bits_per_pixel_x16 = bits_per_pixel; -+ s->dsc_sink_cap.block_pred = 1; -+ s->dsc_sink_cap.native_420 = 0; -+ -+ memcpy(&s->pps, hdmi->link_cfg.pps_payload, 128); -+} -+#endif -+ -+///////////////////////////////////////////////////////////////////////////////////////// -+ -+static int rockchip_hdmi_update_phy_table(struct rockchip_hdmi *hdmi, -+ u32 *config, -+ int phy_table_size) -+{ -+ int i; -+ -+ if (phy_table_size > ARRAY_SIZE(rockchip_phy_config)) { -+ dev_err(hdmi->dev, "phy table array number is out of range\n"); -+ return -E2BIG; -+ } -+ -+ for (i = 0; i < phy_table_size; i++) { -+ if (config[i * 4] != 0) -+ rockchip_phy_config[i].mpixelclock = (u64)config[i * 4]; -+ else -+ rockchip_phy_config[i].mpixelclock = ~0UL; -+ rockchip_phy_config[i].sym_ctr = (u16)config[i * 4 + 1]; -+ rockchip_phy_config[i].term = (u16)config[i * 4 + 2]; -+ rockchip_phy_config[i].vlev_ctr = (u16)config[i * 4 + 3]; -+ } -+ -+ return 0; -+} -+ -+static void repo_hpd_event(struct work_struct *p_work) -+{ -+ struct rockchip_hdmi *hdmi = container_of(p_work, struct rockchip_hdmi, work.work); -+ bool change; -+ -+ change = drm_helper_hpd_irq_event(hdmi->drm_dev); -+ if (change) { -+ dev_dbg(hdmi->dev, "hpd stat changed:%d\n", hdmi->hpd_stat); -+ dw_hdmi_qp_cec_set_hpd(hdmi->hdmi_qp, hdmi->hpd_stat, change); - } -+} -+ -+static irqreturn_t rockchip_hdmi_hardirq(int irq, void *dev_id) -+{ -+ struct rockchip_hdmi *hdmi = dev_id; -+ u32 intr_stat, val; -+ -+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); -+ -+ if (intr_stat) { -+ dev_dbg(hdmi->dev, "hpd irq %#x\n", intr_stat); -+ -+ if (!hdmi->id) -+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, -+ RK3588_HDMI0_HPD_INT_MSK); -+ else -+ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, -+ RK3588_HDMI1_HPD_INT_MSK); -+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); -+ return IRQ_WAKE_THREAD; -+ } -+ -+ return IRQ_NONE; -+} -+ -+static irqreturn_t rockchip_hdmi_irq(int irq, void *dev_id) -+{ -+ struct rockchip_hdmi *hdmi = dev_id; -+ u32 intr_stat, val; -+ int msecs; -+ bool stat; -+ -+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); -+ -+ if (!intr_stat) -+ return IRQ_NONE; -+ -+ if (!hdmi->id) { -+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, -+ RK3588_HDMI0_HPD_INT_CLR); -+ if (intr_stat & RK3588_HDMI0_LEVEL_INT) -+ stat = true; -+ else -+ stat = false; -+ } else { -+ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, -+ RK3588_HDMI1_HPD_INT_CLR); -+ if (intr_stat & RK3588_HDMI1_LEVEL_INT) -+ stat = true; -+ else -+ stat = false; -+ } -+ -+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); -+ -+ if (stat) { -+ hdmi->hpd_stat = true; -+ msecs = 150; -+ } else { -+ hdmi->hpd_stat = false; -+ msecs = 20; -+ } -+ mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs)); -+ -+ if (!hdmi->id) { -+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, -+ RK3588_HDMI0_HPD_INT_CLR) | -+ HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); -+ } else { -+ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, -+ RK3588_HDMI1_HPD_INT_CLR) | -+ HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); -+ } -+ -+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); -+ -+ return IRQ_HANDLED; -+} -+ -+static void init_hpd_work(struct rockchip_hdmi *hdmi) -+{ -+ hdmi->workqueue = create_workqueue("hpd_queue"); -+ INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event); -+} -+ -+static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) -+{ -+ int ret, val, phy_table_size; -+ u32 *phy_config; -+ struct device_node *np = hdmi->dev->of_node; -+ -+ hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); -+ if (IS_ERR(hdmi->regmap)) { -+ DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n"); -+ return PTR_ERR(hdmi->regmap); -+ } -+ -+ if (hdmi->is_hdmi_qp) { -+ hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf"); -+ if (IS_ERR(hdmi->vo1_regmap)) { -+ DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,vo1_grf\n"); -+ return PTR_ERR(hdmi->vo1_regmap); -+ } -+ } -+ -+ hdmi->phyref_clk = devm_clk_get(hdmi->dev, "vpll"); -+ if (PTR_ERR(hdmi->phyref_clk) == -ENOENT) -+ hdmi->phyref_clk = devm_clk_get(hdmi->dev, "ref"); -+ -+ if (PTR_ERR(hdmi->phyref_clk) == -ENOENT) { -+ hdmi->phyref_clk = NULL; -+ } else if (PTR_ERR(hdmi->phyref_clk) == -EPROBE_DEFER) { -+ return -EPROBE_DEFER; -+ } else if (IS_ERR(hdmi->phyref_clk)) { -+ DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n"); -+ return PTR_ERR(hdmi->phyref_clk); -+ } -+ -+ hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf"); -+ if (PTR_ERR(hdmi->grf_clk) == -ENOENT) { -+ hdmi->grf_clk = NULL; -+ } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) { -+ return -EPROBE_DEFER; -+ } else if (IS_ERR(hdmi->grf_clk)) { -+ DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n"); -+ return PTR_ERR(hdmi->grf_clk); -+ } -+ -+ hdmi->hclk_vio = devm_clk_get(hdmi->dev, "hclk_vio"); -+ if (PTR_ERR(hdmi->hclk_vio) == -ENOENT) { -+ hdmi->hclk_vio = NULL; -+ } else if (PTR_ERR(hdmi->hclk_vio) == -EPROBE_DEFER) { -+ return -EPROBE_DEFER; -+ } else if (IS_ERR(hdmi->hclk_vio)) { -+ dev_err(hdmi->dev, "failed to get hclk_vio clock\n"); -+ return PTR_ERR(hdmi->hclk_vio); -+ } -+ -+ hdmi->hclk_vop = devm_clk_get(hdmi->dev, "hclk"); -+ if (PTR_ERR(hdmi->hclk_vop) == -ENOENT) { -+ hdmi->hclk_vop = NULL; -+ } else if (PTR_ERR(hdmi->hclk_vop) == -EPROBE_DEFER) { -+ return -EPROBE_DEFER; -+ } else if (IS_ERR(hdmi->hclk_vop)) { -+ dev_err(hdmi->dev, "failed to get hclk_vop clock\n"); -+ return PTR_ERR(hdmi->hclk_vop); -+ } -+ -+ hdmi->aud_clk = devm_clk_get_optional(hdmi->dev, "aud"); -+ if (IS_ERR(hdmi->aud_clk)) { -+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->aud_clk), -+ "failed to get aud_clk clock\n"); -+ return PTR_ERR(hdmi->aud_clk); -+ } -+ -+ hdmi->hpd_clk = devm_clk_get_optional(hdmi->dev, "hpd"); -+ if (IS_ERR(hdmi->hpd_clk)) { -+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hpd_clk), -+ "failed to get hpd_clk clock\n"); -+ return PTR_ERR(hdmi->hpd_clk); -+ } -+ -+ hdmi->hclk_vo1 = devm_clk_get_optional(hdmi->dev, "hclk_vo1"); -+ if (IS_ERR(hdmi->hclk_vo1)) { -+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hclk_vo1), -+ "failed to get hclk_vo1 clock\n"); -+ return PTR_ERR(hdmi->hclk_vo1); -+ } -+ -+ hdmi->earc_clk = devm_clk_get_optional(hdmi->dev, "earc"); -+ if (IS_ERR(hdmi->earc_clk)) { -+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->earc_clk), -+ "failed to get earc_clk clock\n"); -+ return PTR_ERR(hdmi->earc_clk); -+ } -+ -+ hdmi->hdmitx_ref = devm_clk_get_optional(hdmi->dev, "hdmitx_ref"); -+ if (IS_ERR(hdmi->hdmitx_ref)) { -+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmitx_ref), -+ "failed to get hdmitx_ref clock\n"); -+ return PTR_ERR(hdmi->hdmitx_ref); -+ } -+ -+ hdmi->pclk = devm_clk_get_optional(hdmi->dev, "pclk"); -+ if (IS_ERR(hdmi->pclk)) { -+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->pclk), -+ "failed to get pclk clock\n"); -+ return PTR_ERR(hdmi->pclk); -+ } -+ -+ hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable", -+ GPIOD_OUT_HIGH); -+ if (IS_ERR(hdmi->enable_gpio)) { -+ ret = PTR_ERR(hdmi->enable_gpio); -+ dev_err(hdmi->dev, "failed to request enable GPIO: %d\n", ret); -+ return ret; -+ } -+ -+ hdmi->skip_check_420_mode = -+ of_property_read_bool(np, "skip-check-420-mode"); -+ -+ if (of_get_property(np, "rockchip,phy-table", &val)) { -+ phy_config = kmalloc(val, GFP_KERNEL); -+ if (!phy_config) { -+ /* use default table when kmalloc failed. */ -+ dev_err(hdmi->dev, "kmalloc phy table failed\n"); -+ -+ return -ENOMEM; -+ } -+ phy_table_size = val / 16; -+ of_property_read_u32_array(np, "rockchip,phy-table", -+ phy_config, val / sizeof(u32)); -+ ret = rockchip_hdmi_update_phy_table(hdmi, phy_config, -+ phy_table_size); -+ if (ret) { -+ kfree(phy_config); -+ return ret; -+ } -+ kfree(phy_config); -+ } else { -+ dev_dbg(hdmi->dev, "use default hdmi phy table\n"); -+ } -+ -+ return 0; -+} -+ -+static enum drm_mode_status -+dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdm, void *data, -+ const struct drm_display_info *info, -+ const struct drm_display_mode *mode) -+{ -+ struct rockchip_hdmi *hdmi = data; -+ enum drm_mode_status status = MODE_OK; -+ -+ /* -+ * Pixel clocks we support are always < 2GHz and so fit in an -+ * int. We should make sure source rate does too so we don't get -+ * overflow when we multiply by 1000. -+ */ -+ if (mode->clock > INT_MAX / 1000) -+ return MODE_BAD; -+ -+/* -+ if (!encoder) { -+ const struct drm_connector_helper_funcs *funcs; -+ -+ funcs = connector->helper_private; -+ if (funcs->atomic_best_encoder) -+ encoder = funcs->atomic_best_encoder(connector, -+ connector->state); -+ else -+ encoder = funcs->best_encoder(connector); -+ } -+ -+ if (!encoder || !encoder->possible_crtcs) -+ return MODE_BAD; -+ -+ -+ hdmi = to_rockchip_hdmi(encoder); -+*/ -+ -+ /* -+ * If sink max TMDS clock < 340MHz, we should check the mode pixel -+ * clock > 340MHz is YCbCr420 or not and whether the platform supports -+ * YCbCr420. -+ */ -+ if (!hdmi->skip_check_420_mode) { -+ if (mode->clock > 340000 && -+ info->max_tmds_clock < 340000 && -+ !drm_mode_is_420(info, mode)) -+ return MODE_BAD; -+ -+ if (hdmi->max_tmdsclk <= 340000 && mode->clock > 340000 && -+ !drm_mode_is_420(info, mode)) -+ return MODE_BAD; -+ }; -+ -+ if (hdmi->phy) { -+ if (hdmi->is_hdmi_qp) -+ phy_set_bus_width(hdmi->phy, mode->clock * 10); -+ else -+ phy_set_bus_width(hdmi->phy, 8); -+ } -+ -+ /* -+ * ensure all drm display mode can work, if someone want support more -+ * resolutions, please limit the possible_crtc, only connect to -+ * needed crtc. -+ drm_for_each_crtc(crtc, connector->dev) { -+ int pipe = drm_crtc_index(crtc); -+ const struct rockchip_crtc_funcs *funcs = -+ priv->crtc_funcs[pipe]; -+ -+ if (!(encoder->possible_crtcs & drm_crtc_mask(crtc))) -+ continue; -+ if (!funcs || !funcs->mode_valid) -+ continue; -+ -+ status = funcs->mode_valid(crtc, mode, -+ DRM_MODE_CONNECTOR_HDMIA); -+ if (status != MODE_OK) -+ return status; -+ } -+ */ -+ -+ return status; -+} -+ -+static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) -+{ -+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); -+ struct drm_crtc *crtc = encoder->crtc; -+#if 0 -+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); -+ -+ if (crtc->state->active_changed) { -+ if (hdmi->plat_data->split_mode) { -+ s->output_if &= ~(VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1); -+ } else { -+ if (!hdmi->id) -+ s->output_if &= ~VOP_OUTPUT_IF_HDMI1; -+ else -+ s->output_if &= ~VOP_OUTPUT_IF_HDMI0; -+ } -+ } -+#endif -+ /* -+ * when plug out hdmi it will be switch cvbs and then phy bus width -+ * must be set as 8 -+ */ -+ if (hdmi->phy) -+ phy_set_bus_width(hdmi->phy, 8); -+} -+ -+static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) -+{ -+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); -+ struct drm_crtc *crtc = encoder->crtc; -+ u32 val; -+ int mux; -+ int ret; -+ -+ if (WARN_ON(!crtc || !crtc->state)) -+ return; -+ -+ if (hdmi->phy) -+ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); -+ -+ clk_set_rate(hdmi->phyref_clk, -+ crtc->state->adjusted_mode.crtc_clock * 1000); -+ -+ if (hdmi->chip_data->lcdsel_grf_reg < 0) -+ return; -+ -+ mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); -+ if (mux) -+ val = hdmi->chip_data->lcdsel_lit; -+ else -+ val = hdmi->chip_data->lcdsel_big; -+ -+ ret = clk_prepare_enable(hdmi->grf_clk); -+ if (ret < 0) { -+ DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret); -+ return; -+ } -+ -+ ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val); -+ if (ret != 0) -+ DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret); -+ -+ if (hdmi->chip_data->lcdsel_grf_reg == RK3288_GRF_SOC_CON6) { -+ struct rockchip_crtc_state *s = -+ to_rockchip_crtc_state(crtc->state); -+ u32 mode_mask = mux ? RK3288_HDMI_LCDC1_YUV420 : -+ RK3288_HDMI_LCDC0_YUV420; -+ -+ if (s->output_mode == ROCKCHIP_OUT_MODE_YUV420) -+ val = HIWORD_UPDATE(mode_mask, mode_mask); -+ else -+ val = HIWORD_UPDATE(0, mode_mask); -+ -+ regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON16, val); -+ } -+ -+ clk_disable_unprepare(hdmi->grf_clk); -+ DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n", -+ ret ? "LIT" : "BIG"); -+} -+ -+static void rk3588_set_link_mode(struct rockchip_hdmi *hdmi) -+{ -+ int val; -+ bool is_hdmi0; -+ -+ if (!hdmi->id) -+ is_hdmi0 = true; -+ else -+ is_hdmi0 = false; -+ -+ if (!hdmi->link_cfg.frl_mode) { -+ val = HIWORD_UPDATE(0, RK3588_HDMI21_MASK); -+ if (is_hdmi0) -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); -+ else -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); -+ -+ val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); -+ if (is_hdmi0) -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); -+ else -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); -+ -+ return; -+ } -+ -+ val = HIWORD_UPDATE(RK3588_HDMI21_MASK, RK3588_HDMI21_MASK); -+ if (is_hdmi0) -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); -+ else -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); -+ -+ if (hdmi->link_cfg.dsc_mode) { -+ val = HIWORD_UPDATE(RK3588_COMPRESS_MODE_MASK | RK3588_COMPRESSED_DATA, -+ RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); -+ if (is_hdmi0) -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); -+ else -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); -+ } else { -+ val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); -+ if (is_hdmi0) -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); -+ else -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); -+ } -+} -+ -+static void rk3588_set_color_format(struct rockchip_hdmi *hdmi, u64 bus_format, -+ u32 depth) -+{ -+ u32 val = 0; -+ -+ switch (bus_format) { -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ case MEDIA_BUS_FMT_RGB101010_1X30: -+ val = HIWORD_UPDATE(0, RK3588_COLOR_FORMAT_MASK); -+ break; -+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: -+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: -+ val = HIWORD_UPDATE(RK3588_YUV420, RK3588_COLOR_FORMAT_MASK); -+ break; -+ case MEDIA_BUS_FMT_YUV8_1X24: -+ case MEDIA_BUS_FMT_YUV10_1X30: -+ val = HIWORD_UPDATE(RK3588_YUV444, RK3588_COLOR_FORMAT_MASK); -+ break; -+ default: -+ dev_err(hdmi->dev, "can't set correct color format\n"); -+ return; -+ } -+ -+ if (hdmi->link_cfg.dsc_mode) -+ val = HIWORD_UPDATE(RK3588_COMPRESSED_DATA, RK3588_COLOR_FORMAT_MASK); -+ -+ if (depth == 8) -+ val |= HIWORD_UPDATE(RK3588_8BPC, RK3588_COLOR_DEPTH_MASK); -+ else -+ val |= HIWORD_UPDATE(RK3588_10BPC, RK3588_COLOR_DEPTH_MASK); -+ -+ if (!hdmi->id) -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); -+ else -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); -+} -+ -+static void rk3588_set_grf_cfg(void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ int color_depth; -+ -+ rk3588_set_link_mode(hdmi); -+ color_depth = hdmi_bus_fmt_color_depth(hdmi->bus_format); -+ rk3588_set_color_format(hdmi, hdmi->bus_format, color_depth); -+} -+ -+static void -+dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, -+ struct drm_crtc_state *crtc_state, -+ struct rockchip_hdmi *hdmi, -+ unsigned int *color_format, -+ unsigned int *output_mode, -+ unsigned long *bus_format, -+ unsigned int *bus_width, -+ unsigned long *enc_out_encoding, -+ unsigned int *eotf) -+{ -+ struct drm_display_info *info = &conn_state->connector->display_info; -+ struct drm_display_mode mode; -+ struct hdr_output_metadata *hdr_metadata; -+ u32 vic; -+ unsigned long tmdsclock, pixclock; -+ unsigned int color_depth; -+ bool support_dc = false; -+ bool sink_is_hdmi = true; -+ u32 max_tmds_clock = info->max_tmds_clock; -+ int output_eotf; -+ -+ drm_mode_copy(&mode, &crtc_state->mode); -+ pixclock = mode.crtc_clock; -+ -+ vic = drm_match_cea_mode(&mode); -+ -+ if (!hdmi->is_hdmi_qp) -+ sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi); -+ -+ *color_format = RK_IF_FORMAT_RGB; -+ -+ switch (hdmi->hdmi_output) { -+ case RK_IF_FORMAT_YCBCR_HQ: -+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) -+ *color_format = RK_IF_FORMAT_YCBCR444; -+ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) -+ *color_format = RK_IF_FORMAT_YCBCR422; -+ else if (conn_state->connector->ycbcr_420_allowed && -+ drm_mode_is_420(info, &mode) && -+ (pixclock >= 594000 && !hdmi->is_hdmi_qp)) -+ *color_format = RK_IF_FORMAT_YCBCR420; -+ break; -+ case RK_IF_FORMAT_YCBCR_LQ: -+ if (conn_state->connector->ycbcr_420_allowed && -+ drm_mode_is_420(info, &mode) && pixclock >= 594000) -+ *color_format = RK_IF_FORMAT_YCBCR420; -+ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) -+ *color_format = RK_IF_FORMAT_YCBCR422; -+ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) -+ *color_format = RK_IF_FORMAT_YCBCR444; -+ break; -+ case RK_IF_FORMAT_YCBCR420: -+ if (conn_state->connector->ycbcr_420_allowed && -+ drm_mode_is_420(info, &mode) && pixclock >= 594000) -+ *color_format = RK_IF_FORMAT_YCBCR420; -+ break; -+ case RK_IF_FORMAT_YCBCR422: -+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) -+ *color_format = RK_IF_FORMAT_YCBCR422; -+ break; -+ case RK_IF_FORMAT_YCBCR444: -+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) -+ *color_format = RK_IF_FORMAT_YCBCR444; -+ break; -+ case RK_IF_FORMAT_RGB: -+ default: -+ break; -+ } -+ -+ if (*color_format == RK_IF_FORMAT_RGB && -+ info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) -+ support_dc = true; -+ if (*color_format == RK_IF_FORMAT_YCBCR444 && -+ info->edid_hdmi_ycbcr444_dc_modes & -+ (DRM_EDID_HDMI_DC_Y444 | DRM_EDID_HDMI_DC_30)) -+ support_dc = true; -+ if (*color_format == RK_IF_FORMAT_YCBCR422) -+ support_dc = true; -+ if (*color_format == RK_IF_FORMAT_YCBCR420 && -+ info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) -+ support_dc = true; -+ -+ if (hdmi->colordepth > 8 && support_dc) -+ color_depth = 10; -+ else -+ color_depth = 8; -+ -+ if (!sink_is_hdmi) { -+ *color_format = RK_IF_FORMAT_RGB; -+ color_depth = 8; -+ } -+ -+ *eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR; -+ if (conn_state->hdr_output_metadata) { -+ hdr_metadata = (struct hdr_output_metadata *) -+ conn_state->hdr_output_metadata->data; -+ output_eotf = hdr_metadata->hdmi_metadata_type1.eotf; -+ if (output_eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR && -+ output_eotf <= HDMI_EOTF_BT_2100_HLG) -+ *eotf = output_eotf; -+ } -+ -+ hdmi->colorimetry = conn_state->colorspace; -+ -+ if ((*eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR && -+ conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf & -+ BIT(*eotf)) || ((hdmi->colorimetry >= DRM_MODE_COLORIMETRY_BT2020_CYCC) && -+ (hdmi->colorimetry <= DRM_MODE_COLORIMETRY_BT2020_YCC))) -+ *enc_out_encoding = V4L2_YCBCR_ENC_BT2020; -+ else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) || -+ (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) -+ *enc_out_encoding = V4L2_YCBCR_ENC_601; -+ else -+ *enc_out_encoding = V4L2_YCBCR_ENC_709; -+ -+ if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { -+ /* BT2020 require color depth at lest 10bit */ -+ color_depth = 10; -+ /* We prefer use YCbCr422 to send 10bit */ -+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) -+ *color_format = RK_IF_FORMAT_YCBCR422; -+ if (hdmi->is_hdmi_qp) { -+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR420) { -+ if (mode.clock >= 340000) -+ *color_format = RK_IF_FORMAT_YCBCR420; -+ else -+ *color_format = RK_IF_FORMAT_RGB; -+ } else { -+ *color_format = RK_IF_FORMAT_RGB; -+ } -+ } -+ } -+ -+ if (mode.flags & DRM_MODE_FLAG_DBLCLK) -+ pixclock *= 2; -+ if ((mode.flags & DRM_MODE_FLAG_3D_MASK) == -+ DRM_MODE_FLAG_3D_FRAME_PACKING) -+ pixclock *= 2; -+ -+ if (*color_format == RK_IF_FORMAT_YCBCR422 || color_depth == 8) -+ tmdsclock = pixclock; -+ else -+ tmdsclock = pixclock * (color_depth) / 8; -+ -+ if (*color_format == RK_IF_FORMAT_YCBCR420) -+ tmdsclock /= 2; -+ -+ /* XXX: max_tmds_clock of some sink is 0, we think it is 340MHz. */ -+ if (!max_tmds_clock) -+ max_tmds_clock = 340000; -+ -+ max_tmds_clock = min(max_tmds_clock, hdmi->max_tmdsclk); -+ -+ if ((tmdsclock > max_tmds_clock) && !hdmi->is_hdmi_qp) { -+ if (max_tmds_clock >= 594000) { -+ color_depth = 8; -+ } else if (max_tmds_clock > 340000) { -+ if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000) -+ *color_format = RK_IF_FORMAT_YCBCR420; -+ } else { -+ color_depth = 8; -+ if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000) -+ *color_format = RK_IF_FORMAT_YCBCR420; -+ } -+ } -+ -+ if (hdmi->is_hdmi_qp) { -+ if (mode.clock >= 340000) { -+ if (drm_mode_is_420(info, &mode)) -+ *color_format = RK_IF_FORMAT_YCBCR420; -+ else -+ *color_format = RK_IF_FORMAT_RGB; -+ } else if (tmdsclock > max_tmds_clock) { -+ color_depth = 8; -+ if (drm_mode_is_420(info, &mode)) -+ *color_format = RK_IF_FORMAT_YCBCR420; -+ } -+ } -+ -+ dev_info(hdmi->dev, "%s color_format: %d\n", __func__, *color_format); -+ if (*color_format == RK_IF_FORMAT_YCBCR420) { -+ *output_mode = ROCKCHIP_OUT_MODE_YUV420; -+ if (color_depth > 8) -+ *bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30; -+ else -+ *bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24; -+ *bus_width = color_depth / 2; -+ } else { -+ *output_mode = ROCKCHIP_OUT_MODE_AAAA; -+ if (color_depth > 8) { -+ if (*color_format != RK_IF_FORMAT_RGB && -+ !hdmi->unsupported_yuv_input) -+ *bus_format = MEDIA_BUS_FMT_YUV10_1X30; -+ else -+ *bus_format = MEDIA_BUS_FMT_RGB101010_1X30; -+ } else { -+ if (*color_format != RK_IF_FORMAT_RGB && -+ !hdmi->unsupported_yuv_input) -+ *bus_format = MEDIA_BUS_FMT_YUV8_1X24; -+ else -+ *bus_format = MEDIA_BUS_FMT_RGB888_1X24; -+ } -+ if (*color_format == RK_IF_FORMAT_YCBCR422) -+ *bus_width = 8; -+ else -+ *bus_width = color_depth; -+ } -+ -+ hdmi->bus_format = *bus_format; -+ -+ if (*color_format == RK_IF_FORMAT_YCBCR422) { -+ if (color_depth == 12) -+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; -+ else if (color_depth == 10) -+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; -+ else -+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; -+ } else { -+ hdmi->output_bus_format = *bus_format; -+ } -+} -+ -+static bool -+dw_hdmi_rockchip_check_color(struct drm_connector_state *conn_state, -+ struct rockchip_hdmi *hdmi) -+{ -+ struct drm_crtc_state *crtc_state = conn_state->crtc->state; -+ unsigned int colorformat; -+ unsigned long bus_format; -+ unsigned long output_bus_format = hdmi->output_bus_format; -+ unsigned long enc_out_encoding = hdmi->enc_out_encoding; -+ unsigned int eotf, bus_width; -+ unsigned int output_mode; -+ -+ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi, -+ &colorformat, -+ &output_mode, &bus_format, &bus_width, -+ &hdmi->enc_out_encoding, &eotf); -+ -+ if (output_bus_format != hdmi->output_bus_format || -+ enc_out_encoding != hdmi->enc_out_encoding) -+ return true; -+ else -+ return false; -+} -+ -+static int -+dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, -+ struct drm_crtc_state *crtc_state, -+ struct drm_connector_state *conn_state) -+{ -+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); -+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); -+ unsigned int colorformat, bus_width, tmdsclk; -+ struct drm_display_mode mode; -+ unsigned int output_mode; -+ unsigned long bus_format; -+ int color_depth; -+ int eotf; -+ bool secondary = false; -+ -+ /* -+ * There are two hdmi but only one encoder in split mode, -+ * so we need to check twice. -+ */ -+secondary: -+ drm_mode_copy(&mode, &crtc_state->mode); -+ -+ hdmi->vp_id = 0; -+ -+ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi, -+ &colorformat, -+ &output_mode, &bus_format, &bus_width, -+ &hdmi->enc_out_encoding, &eotf); -+ -+ s->bus_format = bus_format; -+ if (hdmi->is_hdmi_qp) { -+ color_depth = hdmi_bus_fmt_color_depth(bus_format); -+ tmdsclk = hdmi_get_tmdsclock(hdmi, crtc_state->mode.clock); -+ if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format)) -+ tmdsclk /= 2; -+ hdmi_select_link_config(hdmi, crtc_state, tmdsclk); -+ -+ if (hdmi->link_cfg.frl_mode) { -+ gpiod_set_value(hdmi->enable_gpio, 0); -+ /* in the current version, support max 40G frl */ -+ if (hdmi->link_cfg.rate_per_lane >= 10) { -+ hdmi->link_cfg.frl_lanes = 4; -+ hdmi->link_cfg.rate_per_lane = 10; -+ } -+ bus_width = hdmi->link_cfg.frl_lanes * -+ hdmi->link_cfg.rate_per_lane * 1000000; -+ /* 10 bit color depth and frl mode */ -+ if (color_depth == 10) -+ bus_width |= -+ COLOR_DEPTH_10BIT | HDMI_FRL_MODE; -+ else -+ bus_width |= HDMI_FRL_MODE; -+ } else { -+ gpiod_set_value(hdmi->enable_gpio, 1); -+ bus_width = hdmi_get_tmdsclock(hdmi, mode.clock * 10); -+ if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format)) -+ bus_width /= 2; -+ -+ if (color_depth == 10) -+ bus_width |= COLOR_DEPTH_10BIT; -+ } -+ } -+ -+ hdmi->phy_bus_width = bus_width; -+ -+ if (hdmi->phy) -+ phy_set_bus_width(hdmi->phy, bus_width); -+ -+ s->output_type = DRM_MODE_CONNECTOR_HDMIA; -+ -+#if 0 -+ s->tv_state = &conn_state->tv; -+ -+ if (hdmi->plat_data->split_mode) { -+ s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE; -+ if (hdmi->plat_data->right && hdmi->id) -+ s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP; -+ s->output_if |= VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1; -+ } else { -+ if (!hdmi->id) -+ s->output_if |= VOP_OUTPUT_IF_HDMI0; -+ else -+ s->output_if |= VOP_OUTPUT_IF_HDMI1; -+ } -+#endif -+ -+ s->output_mode = output_mode; -+ hdmi->bus_format = s->bus_format; -+ -+ if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_BT2020) -+ s->color_space = V4L2_COLORSPACE_BT2020; -+ else if (colorformat == RK_IF_FORMAT_RGB) -+ s->color_space = V4L2_COLORSPACE_DEFAULT; -+ else if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_709) -+ s->color_space = V4L2_COLORSPACE_REC709; -+ else -+ s->color_space = V4L2_COLORSPACE_SMPTE170M; -+ -+ if (hdmi->plat_data->split_mode && !secondary) { -+ hdmi = rockchip_hdmi_find_by_id(hdmi->dev->driver, !hdmi->id); -+ secondary = true; -+ goto secondary; -+ } -+ -+ return 0; -+} -+ -+ -+static unsigned long -+dw_hdmi_rockchip_get_input_bus_format(void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ return hdmi->bus_format; -+} -+ -+static unsigned long -+dw_hdmi_rockchip_get_output_bus_format(void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ return hdmi->output_bus_format; -+} -+ -+static unsigned long -+dw_hdmi_rockchip_get_enc_in_encoding(void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ return hdmi->enc_out_encoding; -+} -+ -+static unsigned long -+dw_hdmi_rockchip_get_enc_out_encoding(void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ return hdmi->enc_out_encoding; -+} -+ -+static unsigned long -+dw_hdmi_rockchip_get_quant_range(void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ return hdmi->hdmi_quant_range; -+} -+ -+static struct drm_property * -+dw_hdmi_rockchip_get_hdr_property(void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ return hdmi->hdr_panel_metadata_property; -+} -+ -+static struct drm_property_blob * -+dw_hdmi_rockchip_get_hdr_blob(void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ return hdmi->hdr_panel_blob_ptr; -+} -+ -+static bool -+dw_hdmi_rockchip_get_color_changed(void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ bool ret = false; -+ -+ if (hdmi->color_changed) -+ ret = true; -+ hdmi->color_changed = 0; -+ -+ return ret; -+} -+ -+#if 0 -+static int -+dw_hdmi_rockchip_get_edid_dsc_info(void *data, struct edid *edid) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ if (!edid) -+ return -EINVAL; -+ -+ return rockchip_drm_parse_cea_ext(&hdmi->dsc_cap, -+ &hdmi->max_frl_rate_per_lane, -+ &hdmi->max_lanes, edid); -+} -+ -+static int -+dw_hdmi_rockchip_get_next_hdr_data(void *data, struct edid *edid, -+ struct drm_connector *connector) -+{ -+ int ret; -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ struct next_hdr_sink_data *sink_data = &hdmi->next_hdr_data; -+ size_t size = sizeof(*sink_data); -+ struct drm_property *property = hdmi->next_hdr_sink_data_property; -+ struct drm_property_blob *blob = hdmi->hdr_panel_blob_ptr; -+ -+ if (!edid) -+ return -EINVAL; -+ -+ rockchip_drm_parse_next_hdr(sink_data, edid); -+ -+ ret = drm_property_replace_global_blob(connector->dev, &blob, size, sink_data, -+ &connector->base, property); -+ -+ return ret; -+}; -+#endif -+ -+static -+struct dw_hdmi_link_config *dw_hdmi_rockchip_get_link_cfg(void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ return &hdmi->link_cfg; -+} -+ -+static int dw_hdmi_dclk_set(void *data, bool enable) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ char clk_name[16]; -+ struct clk *dclk; -+ int ret; -+ -+ printk("%s vp%d\n", __func__, hdmi->vp_id); -+ snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", hdmi->vp_id); -+ -+ dclk = devm_clk_get(hdmi->dev, clk_name); -+ if (IS_ERR(dclk)) { -+ DRM_DEV_ERROR(hdmi->dev, "failed to get %s\n", clk_name); -+ return PTR_ERR(dclk); -+ } -+ -+ if (enable) { -+ ret = clk_prepare_enable(dclk); -+ if (ret < 0) -+ DRM_DEV_ERROR(hdmi->dev, "failed to enable dclk for video port%d - %d\n", -+ hdmi->vp_id, ret); -+ } else { -+ clk_disable_unprepare(dclk); -+ } -+ -+ return 0; -+} -+ -+static const struct drm_prop_enum_list color_depth_enum_list[] = { -+ { 0, "Automatic" }, /* Prefer highest color depth */ -+ { 8, "24bit" }, -+ { 10, "30bit" }, -+}; -+ -+static const struct drm_prop_enum_list drm_hdmi_output_enum_list[] = { -+ { RK_IF_FORMAT_RGB, "rgb" }, -+ { RK_IF_FORMAT_YCBCR444, "ycbcr444" }, -+ { RK_IF_FORMAT_YCBCR422, "ycbcr422" }, -+ { RK_IF_FORMAT_YCBCR420, "ycbcr420" }, -+ { RK_IF_FORMAT_YCBCR_HQ, "ycbcr_high_subsampling" }, -+ { RK_IF_FORMAT_YCBCR_LQ, "ycbcr_low_subsampling" }, -+ { RK_IF_FORMAT_MAX, "invalid_output" }, -+}; -+ -+static const struct drm_prop_enum_list quant_range_enum_list[] = { -+ { HDMI_QUANTIZATION_RANGE_DEFAULT, "default" }, -+ { HDMI_QUANTIZATION_RANGE_LIMITED, "limit" }, -+ { HDMI_QUANTIZATION_RANGE_FULL, "full" }, -+}; -+ -+static const struct drm_prop_enum_list output_hdmi_dvi_enum_list[] = { -+ { 0, "auto" }, -+ { 1, "force_hdmi" }, -+ { 2, "force_dvi" }, -+}; -+ -+static const struct drm_prop_enum_list output_type_cap_list[] = { -+ { 0, "DVI" }, -+ { 1, "HDMI" }, -+}; -+ -+static void -+dw_hdmi_rockchip_attach_properties(struct drm_connector *connector, -+ unsigned int color, int version, -+ void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ struct drm_property *prop; -+ -+ switch (color) { -+ case MEDIA_BUS_FMT_RGB101010_1X30: -+ hdmi->hdmi_output = RK_IF_FORMAT_RGB; -+ hdmi->colordepth = 10; -+ break; -+ case MEDIA_BUS_FMT_YUV8_1X24: -+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444; -+ hdmi->colordepth = 8; -+ break; -+ case MEDIA_BUS_FMT_YUV10_1X30: -+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444; -+ hdmi->colordepth = 10; -+ break; -+ case MEDIA_BUS_FMT_UYVY10_1X20: -+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422; -+ hdmi->colordepth = 10; -+ break; -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422; -+ hdmi->colordepth = 8; -+ break; -+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: -+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420; -+ hdmi->colordepth = 8; -+ break; -+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: -+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420; -+ hdmi->colordepth = 10; -+ break; -+ default: -+ hdmi->hdmi_output = RK_IF_FORMAT_RGB; -+ hdmi->colordepth = 8; -+ } -+ -+ hdmi->bus_format = color; -+ -+ if (hdmi->hdmi_output == RK_IF_FORMAT_YCBCR422) { -+ if (hdmi->colordepth == 12) -+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; -+ else if (hdmi->colordepth == 10) -+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; -+ else -+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; -+ } else { -+ hdmi->output_bus_format = hdmi->bus_format; -+ } -+ -+ prop = connector->dev->mode_config.hdr_output_metadata_property; -+ if (version >= 0x211a || hdmi->is_hdmi_qp) -+ drm_object_attach_property(&connector->base, prop, 0); -+ -+ if (!drm_mode_create_hdmi_colorspace_property(connector, 0)) -+ drm_object_attach_property(&connector->base, -+ connector->colorspace_property, 0); -+} - -- hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref"); -- if (!hdmi->ref_clk) -- hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll"); -+static void -+dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector, -+ void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; - -- if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) { -- return -EPROBE_DEFER; -- } else if (IS_ERR(hdmi->ref_clk)) { -- DRM_DEV_ERROR(hdmi->dev, "failed to get reference clock\n"); -- return PTR_ERR(hdmi->ref_clk); -+ if (hdmi->color_depth_property) { -+ drm_property_destroy(connector->dev, -+ hdmi->color_depth_property); -+ hdmi->color_depth_property = NULL; - } - -- hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf"); -- if (PTR_ERR(hdmi->grf_clk) == -ENOENT) { -- hdmi->grf_clk = NULL; -- } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) { -- return -EPROBE_DEFER; -- } else if (IS_ERR(hdmi->grf_clk)) { -- DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n"); -- return PTR_ERR(hdmi->grf_clk); -+ if (hdmi->hdmi_output_property) { -+ drm_property_destroy(connector->dev, -+ hdmi->hdmi_output_property); -+ hdmi->hdmi_output_property = NULL; - } - -- hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9"); -- if (IS_ERR(hdmi->avdd_0v9)) -- return PTR_ERR(hdmi->avdd_0v9); -+ if (hdmi->colordepth_capacity) { -+ drm_property_destroy(connector->dev, -+ hdmi->colordepth_capacity); -+ hdmi->colordepth_capacity = NULL; -+ } - -- hdmi->avdd_1v8 = devm_regulator_get(hdmi->dev, "avdd-1v8"); -- if (IS_ERR(hdmi->avdd_1v8)) -- return PTR_ERR(hdmi->avdd_1v8); -+ if (hdmi->outputmode_capacity) { -+ drm_property_destroy(connector->dev, -+ hdmi->outputmode_capacity); -+ hdmi->outputmode_capacity = NULL; -+ } - -- return 0; --} -+ if (hdmi->quant_range) { -+ drm_property_destroy(connector->dev, -+ hdmi->quant_range); -+ hdmi->quant_range = NULL; -+ } - --static enum drm_mode_status --dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, -- const struct drm_display_info *info, -- const struct drm_display_mode *mode) --{ -- struct rockchip_hdmi *hdmi = data; -- const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg; -- int pclk = mode->clock * 1000; -- bool exact_match = hdmi->plat_data->phy_force_vendor; -- int i; -+ if (hdmi->hdr_panel_metadata_property) { -+ drm_property_destroy(connector->dev, -+ hdmi->hdr_panel_metadata_property); -+ hdmi->hdr_panel_metadata_property = NULL; -+ } - -- if (hdmi->ref_clk) { -- int rpclk = clk_round_rate(hdmi->ref_clk, pclk); -+ if (hdmi->next_hdr_sink_data_property) { -+ drm_property_destroy(connector->dev, -+ hdmi->next_hdr_sink_data_property); -+ hdmi->next_hdr_sink_data_property = NULL; -+ } - -- if (abs(rpclk - pclk) > pclk / 1000) -- return MODE_NOCLOCK; -+ if (hdmi->output_hdmi_dvi) { -+ drm_property_destroy(connector->dev, -+ hdmi->output_hdmi_dvi); -+ hdmi->output_hdmi_dvi = NULL; - } - -- for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) { -- /* -- * For vendor specific phys force an exact match of the pixelclock -- * to preserve the original behaviour of the driver. -- */ -- if (exact_match && pclk == mpll_cfg[i].mpixelclock) -- return MODE_OK; -- /* -- * The Synopsys phy can work with pixelclocks up to the value given -- * in the corresponding mpll_cfg entry. -- */ -- if (!exact_match && pclk <= mpll_cfg[i].mpixelclock) -- return MODE_OK; -+ if (hdmi->output_type_capacity) { -+ drm_property_destroy(connector->dev, -+ hdmi->output_type_capacity); -+ hdmi->output_type_capacity = NULL; - } - -- return MODE_BAD; -+ if (hdmi->user_split_mode_prop) { -+ drm_property_destroy(connector->dev, -+ hdmi->user_split_mode_prop); -+ hdmi->user_split_mode_prop = NULL; -+ } - } - --static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) -+static int -+dw_hdmi_rockchip_set_property(struct drm_connector *connector, -+ struct drm_connector_state *state, -+ struct drm_property *property, -+ u64 val, -+ void *data) - { --} -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ struct drm_mode_config *config = &connector->dev->mode_config; -+ -+ if (property == hdmi->color_depth_property) { -+ hdmi->colordepth = val; -+ /* If hdmi is disconnected, state->crtc is null */ -+ if (!state->crtc) -+ return 0; -+ if (dw_hdmi_rockchip_check_color(state, hdmi)) -+ hdmi->color_changed++; -+ return 0; -+ } else if (property == hdmi->hdmi_output_property) { -+ hdmi->hdmi_output = val; -+ if (!state->crtc) -+ return 0; -+ if (dw_hdmi_rockchip_check_color(state, hdmi)) -+ hdmi->color_changed++; -+ return 0; -+ } else if (property == hdmi->quant_range) { -+ u64 quant_range = hdmi->hdmi_quant_range; -+ -+ hdmi->hdmi_quant_range = val; -+ if (quant_range != hdmi->hdmi_quant_range) -+ dw_hdmi_set_quant_range(hdmi->hdmi); -+ return 0; -+ } else if (property == config->hdr_output_metadata_property) { -+ return 0; -+ } else if (property == hdmi->output_hdmi_dvi) { -+ if (hdmi->force_output != val) -+ hdmi->color_changed++; -+ hdmi->force_output = val; -+ dw_hdmi_set_output_type(hdmi->hdmi, val); -+ return 0; -+ } else if (property == hdmi->colordepth_capacity) { -+ return 0; -+ } else if (property == hdmi->outputmode_capacity) { -+ return 0; -+ } else if (property == hdmi->output_type_capacity) { -+ return 0; -+ } - --static bool --dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder, -- const struct drm_display_mode *mode, -- struct drm_display_mode *adj_mode) --{ -- return true; -+ DRM_ERROR("Unknown property [PROP:%d:%s]\n", -+ property->base.id, property->name); -+ -+ return -EINVAL; - } - --static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, -- struct drm_display_mode *mode, -- struct drm_display_mode *adj_mode) -+static int -+dw_hdmi_rockchip_get_property(struct drm_connector *connector, -+ const struct drm_connector_state *state, -+ struct drm_property *property, -+ u64 *val, -+ void *data) - { -- struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ struct drm_display_info *info = &connector->display_info; -+ struct drm_mode_config *config = &connector->dev->mode_config; -+ -+ if (property == hdmi->color_depth_property) { -+ *val = hdmi->colordepth; -+ return 0; -+ } else if (property == hdmi->hdmi_output_property) { -+ *val = hdmi->hdmi_output; -+ return 0; -+ } else if (property == hdmi->colordepth_capacity) { -+ *val = BIT(RK_IF_DEPTH_8); -+ /* RK3368 only support 8bit */ -+ if (hdmi->unsupported_deep_color) -+ return 0; -+ if ((info->edid_hdmi_rgb444_dc_modes | info->edid_hdmi_ycbcr444_dc_modes) & DRM_EDID_HDMI_DC_30) -+ *val |= BIT(RK_IF_DEPTH_10); -+ if ((info->edid_hdmi_rgb444_dc_modes | info->edid_hdmi_ycbcr444_dc_modes) & DRM_EDID_HDMI_DC_36) -+ *val |= BIT(RK_IF_DEPTH_12); -+ if ((info->edid_hdmi_rgb444_dc_modes | info->edid_hdmi_ycbcr444_dc_modes) & DRM_EDID_HDMI_DC_48) -+ *val |= BIT(RK_IF_DEPTH_16); -+ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) -+ *val |= BIT(RK_IF_DEPTH_420_10); -+ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) -+ *val |= BIT(RK_IF_DEPTH_420_12); -+ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48) -+ *val |= BIT(RK_IF_DEPTH_420_16); -+ return 0; -+ } else if (property == hdmi->outputmode_capacity) { -+ *val = BIT(RK_IF_FORMAT_RGB); -+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) -+ *val |= BIT(RK_IF_FORMAT_YCBCR444); -+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) -+ *val |= BIT(RK_IF_FORMAT_YCBCR422); -+ if (connector->ycbcr_420_allowed && -+ info->color_formats & DRM_COLOR_FORMAT_YCBCR420) -+ *val |= BIT(RK_IF_FORMAT_YCBCR420); -+ return 0; -+ } else if (property == hdmi->quant_range) { -+ *val = hdmi->hdmi_quant_range; -+ return 0; -+ } else if (property == config->hdr_output_metadata_property) { -+ *val = state->hdr_output_metadata ? -+ state->hdr_output_metadata->base.id : 0; -+ return 0; -+ } else if (property == hdmi->output_hdmi_dvi) { -+ *val = hdmi->force_output; -+ return 0; -+ } else if (property == hdmi->output_type_capacity) { -+ *val = dw_hdmi_get_output_type_cap(hdmi->hdmi); -+ return 0; -+ } else if (property == hdmi->user_split_mode_prop) { -+ *val = hdmi->user_split_mode; -+ return 0; -+ } - -- clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000); -+ DRM_ERROR("Unknown property [PROP:%d:%s]\n", -+ property->base.id, property->name); -+ -+ return -EINVAL; - } - --static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) -+static const struct dw_hdmi_property_ops dw_hdmi_rockchip_property_ops = { -+ .attach_properties = dw_hdmi_rockchip_attach_properties, -+ .destroy_properties = dw_hdmi_rockchip_destroy_properties, -+ .set_property = dw_hdmi_rockchip_set_property, -+ .get_property = dw_hdmi_rockchip_get_property, -+}; -+ -+static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, -+ struct drm_display_mode *mode, -+ struct drm_display_mode *adj) - { - struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); -- u32 val; -- int ret; -+ struct drm_crtc *crtc; -+ struct rockchip_crtc_state *s; - -- if (hdmi->chip_data->lcdsel_grf_reg < 0) -+ if (!encoder->crtc) - return; -+ crtc = encoder->crtc; - -- ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); -- if (ret) -- val = hdmi->chip_data->lcdsel_lit; -- else -- val = hdmi->chip_data->lcdsel_big; -- -- ret = clk_prepare_enable(hdmi->grf_clk); -- if (ret < 0) { -- DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret); -+ if (!crtc->state) - return; -- } -- -- ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val); -- if (ret != 0) -- DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret); -+ s = to_rockchip_crtc_state(crtc->state); - -- clk_disable_unprepare(hdmi->grf_clk); -- DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n", -- ret ? "LIT" : "BIG"); --} -+ if (!s) -+ return; - --static int --dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, -- struct drm_crtc_state *crtc_state, -- struct drm_connector_state *conn_state) --{ -- struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); -+ if (hdmi->is_hdmi_qp) { -+ /*s->dsc_enable = 0; -+ if (hdmi->link_cfg.dsc_mode) -+ dw_hdmi_qp_dsc_configure(hdmi, s, crtc->state); */ - -- s->output_mode = ROCKCHIP_OUT_MODE_AAAA; -- s->output_type = DRM_MODE_CONNECTOR_HDMIA; -+ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); -+ } - -- return 0; -+ clk_set_rate(hdmi->phyref_clk, adj->crtc_clock * 1000); - } - - static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = { -- .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup, -- .mode_set = dw_hdmi_rockchip_encoder_mode_set, - .enable = dw_hdmi_rockchip_encoder_enable, - .disable = dw_hdmi_rockchip_encoder_disable, - .atomic_check = dw_hdmi_rockchip_encoder_atomic_check, -+ .mode_set = dw_hdmi_rockchip_encoder_mode_set, - }; - --static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, -- const struct drm_display_info *display, -- const struct drm_display_mode *mode) -+static void -+dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) - { - struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; - -- return phy_power_on(hdmi->phy); -+ while (hdmi->phy->power_count > 0) -+ phy_power_off(hdmi->phy); - } - --static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) -+static int -+dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, -+ const struct drm_display_info *display, -+ const struct drm_display_mode *mode) - { - struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; - -- phy_power_off(hdmi->phy); -+ dw_hdmi_rockchip_genphy_disable(dw_hdmi, data); -+ dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display); -+ return phy_power_on(hdmi->phy); - } - - static void dw_hdmi_rk3228_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) -@@ -436,6 +2586,90 @@ static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) - RK3328_HDMI_HPD_IOE)); - } - -+static void dw_hdmi_qp_rockchip_phy_disable(struct dw_hdmi_qp *dw_hdmi, -+ void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ while (hdmi->phy->power_count > 0) -+ phy_power_off(hdmi->phy); -+} -+ -+static int dw_hdmi_qp_rockchip_genphy_init(struct dw_hdmi_qp *dw_hdmi, void *data, -+ struct drm_display_mode *mode) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ dw_hdmi_qp_rockchip_phy_disable(dw_hdmi, data); -+ -+ return phy_power_on(hdmi->phy); -+} -+ -+static enum drm_connector_status -+dw_hdmi_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) -+{ -+ u32 val; -+ int ret; -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val); -+ -+ if (!hdmi->id) { -+ if (val & RK3588_HDMI0_LEVEL_INT) { -+ hdmi->hpd_stat = true; -+ ret = connector_status_connected; -+ } else { -+ hdmi->hpd_stat = false; -+ ret = connector_status_disconnected; -+ } -+ } else { -+ if (val & RK3588_HDMI1_LEVEL_INT) { -+ hdmi->hpd_stat = true; -+ ret = connector_status_connected; -+ } else { -+ hdmi->hpd_stat = false; -+ ret = connector_status_disconnected; -+ } -+ } -+ -+ return ret; -+} -+ -+static void dw_hdmi_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ u32 val; -+ -+ if (!hdmi->id) { -+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, -+ RK3588_HDMI0_HPD_INT_CLR) | -+ HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); -+ } else { -+ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, -+ RK3588_HDMI1_HPD_INT_CLR) | -+ HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); -+ } -+ -+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); -+} -+ -+static void dw_hdmi_rk3588_phy_set_mode(struct dw_hdmi_qp *dw_hdmi, void *data, -+ u32 mode_mask, bool enable) -+{ -+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; -+ -+ if (!hdmi->phy) -+ return; -+ -+ /* set phy earc/frl mode */ -+ if (enable) -+ hdmi->phy_bus_width |= mode_mask; -+ else -+ hdmi->phy_bus_width &= ~mode_mask; -+ -+ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); -+} -+ - static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = { - .init = dw_hdmi_rockchip_genphy_init, - .disable = dw_hdmi_rockchip_genphy_disable, -@@ -457,6 +2691,8 @@ static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = { - .phy_ops = &rk3228_hdmi_phy_ops, - .phy_name = "inno_dw_hdmi_phy2", - .phy_force_vendor = true, -+ .max_tmdsclk = 371250, -+ .ycbcr_420_allowed = true, - }; - - static struct rockchip_hdmi_chip_data rk3288_chip_data = { -@@ -468,9 +2704,13 @@ static struct rockchip_hdmi_chip_data rk3288_chip_data = { - static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = { - .mode_valid = dw_hdmi_rockchip_mode_valid, - .mpll_cfg = rockchip_mpll_cfg, -+ .mpll_cfg_420 = rockchip_rk3288w_mpll_cfg_420, - .cur_ctr = rockchip_cur_ctr, - .phy_config = rockchip_phy_config, - .phy_data = &rk3288_chip_data, -+ .tmds_n_table = rockchip_werid_tmds_n_table, -+ .unsupported_yuv_input = true, -+ .ycbcr_420_allowed = true, - }; - - static const struct dw_hdmi_phy_ops rk3328_hdmi_phy_ops = { -@@ -495,6 +2735,24 @@ static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = { - .phy_name = "inno_dw_hdmi_phy2", - .phy_force_vendor = true, - .use_drm_infoframe = true, -+ .max_tmdsclk = 371250, -+ .ycbcr_420_allowed = true, -+}; -+ -+static struct rockchip_hdmi_chip_data rk3368_chip_data = { -+ .lcdsel_grf_reg = -1, -+}; -+ -+static const struct dw_hdmi_plat_data rk3368_hdmi_drv_data = { -+ .mode_valid = dw_hdmi_rockchip_mode_valid, -+ .mpll_cfg = rockchip_mpll_cfg, -+ .mpll_cfg_420 = rockchip_mpll_cfg_420, -+ .cur_ctr = rockchip_cur_ctr, -+ .phy_config = rockchip_phy_config, -+ .phy_data = &rk3368_chip_data, -+ .unsupported_deep_color = true, -+ .max_tmdsclk = 340000, -+ .ycbcr_420_allowed = true, - }; - - static struct rockchip_hdmi_chip_data rk3399_chip_data = { -@@ -506,22 +2764,51 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = { - static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = { - .mode_valid = dw_hdmi_rockchip_mode_valid, - .mpll_cfg = rockchip_mpll_cfg, -+ .mpll_cfg_420 = rockchip_mpll_cfg_420, - .cur_ctr = rockchip_cur_ctr, - .phy_config = rockchip_phy_config, - .phy_data = &rk3399_chip_data, - .use_drm_infoframe = true, -+ .ycbcr_420_allowed = true, - }; - - static struct rockchip_hdmi_chip_data rk3568_chip_data = { - .lcdsel_grf_reg = -1, -+ .ddc_en_reg = RK3568_GRF_VO_CON1, - }; - - static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = { - .mode_valid = dw_hdmi_rockchip_mode_valid, - .mpll_cfg = rockchip_mpll_cfg, -+ .mpll_cfg_420 = rockchip_mpll_cfg_420, - .cur_ctr = rockchip_cur_ctr, - .phy_config = rockchip_phy_config, - .phy_data = &rk3568_chip_data, -+ .ycbcr_420_allowed = true, -+ .use_drm_infoframe = true, -+}; -+ -+static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = { -+ .init = dw_hdmi_qp_rockchip_genphy_init, -+ .disable = dw_hdmi_qp_rockchip_phy_disable, -+ .read_hpd = dw_hdmi_rk3588_read_hpd, -+ .setup_hpd = dw_hdmi_rk3588_setup_hpd, -+ .set_mode = dw_hdmi_rk3588_phy_set_mode, -+}; -+ -+struct rockchip_hdmi_chip_data rk3588_hdmi_chip_data = { -+ .lcdsel_grf_reg = -1, -+ .ddc_en_reg = RK3588_GRF_VO1_CON3, -+ .split_mode = true, -+}; -+ -+static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = { -+ .phy_data = &rk3588_hdmi_chip_data, -+ .qp_phy_ops = &rk3588_hdmi_phy_ops, -+ .phy_name = "samsung_hdptx_phy", -+ .phy_force_vendor = true, -+ .ycbcr_420_allowed = true, -+ .is_hdmi_qp = true, - .use_drm_infoframe = true, - }; - -@@ -535,12 +2822,19 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { - { .compatible = "rockchip,rk3328-dw-hdmi", - .data = &rk3328_hdmi_drv_data - }, -+ { -+ .compatible = "rockchip,rk3368-dw-hdmi", -+ .data = &rk3368_hdmi_drv_data -+ }, - { .compatible = "rockchip,rk3399-dw-hdmi", - .data = &rk3399_hdmi_drv_data - }, - { .compatible = "rockchip,rk3568-dw-hdmi", - .data = &rk3568_hdmi_drv_data - }, -+ { .compatible = "rockchip,rk3588-dw-hdmi", -+ .data = &rk3588_hdmi_drv_data -+ }, - {}, - }; - MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids); -@@ -549,45 +2843,99 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, - void *data) - { - struct platform_device *pdev = to_platform_device(dev); -- struct dw_hdmi_plat_data *plat_data; -- const struct of_device_id *match; - struct drm_device *drm = data; - struct drm_encoder *encoder; - struct rockchip_hdmi *hdmi; -+ struct dw_hdmi_plat_data *plat_data; -+ struct rockchip_hdmi *secondary; - int ret; -+ u32 val; - - if (!pdev->dev.of_node) - return -ENODEV; - -- hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); -+ hdmi = platform_get_drvdata(pdev); - if (!hdmi) - return -ENOMEM; - -- match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); -- plat_data = devm_kmemdup(&pdev->dev, match->data, -- sizeof(*plat_data), GFP_KERNEL); -- if (!plat_data) -- return -ENOMEM; -+ plat_data = hdmi->plat_data; -+ hdmi->drm_dev = drm; - -- hdmi->dev = &pdev->dev; -- hdmi->plat_data = plat_data; -- hdmi->chip_data = plat_data->phy_data; - plat_data->phy_data = hdmi; -+ plat_data->get_input_bus_format = -+ dw_hdmi_rockchip_get_input_bus_format; -+ plat_data->get_output_bus_format = -+ dw_hdmi_rockchip_get_output_bus_format; -+ plat_data->get_enc_in_encoding = -+ dw_hdmi_rockchip_get_enc_in_encoding; -+ plat_data->get_enc_out_encoding = -+ dw_hdmi_rockchip_get_enc_out_encoding; -+ plat_data->get_quant_range = -+ dw_hdmi_rockchip_get_quant_range; -+ plat_data->get_hdr_property = -+ dw_hdmi_rockchip_get_hdr_property; -+ plat_data->get_hdr_blob = -+ dw_hdmi_rockchip_get_hdr_blob; -+ plat_data->get_color_changed = -+ dw_hdmi_rockchip_get_color_changed; -+ plat_data->get_link_cfg = dw_hdmi_rockchip_get_link_cfg; -+ plat_data->set_grf_cfg = rk3588_set_grf_cfg; -+ plat_data->dclk_set = dw_hdmi_dclk_set; - plat_data->priv_data = hdmi; -- encoder = &hdmi->encoder.encoder; - -- encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); -- rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder, -- dev->of_node, 0, 0); -+ plat_data->property_ops = &dw_hdmi_rockchip_property_ops; - -- /* -- * If we failed to find the CRTC(s) which this encoder is -- * supposed to be connected to, it's because the CRTC has -- * not been registered yet. Defer probing, and hope that -- * the required CRTC is added later. -- */ -- if (encoder->possible_crtcs == 0) -- return -EPROBE_DEFER; -+ secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id); -+ /* If don't enable hdmi0 and hdmi1, we don't enable split mode */ -+ if (hdmi->chip_data->split_mode && secondary) { -+ -+ /* -+ * hdmi can only attach bridge and init encoder/connector in the -+ * last bind hdmi in split mode, or hdmi->hdmi_qp will not be initialized -+ * and plat_data->left/right will be null pointer. we must check if split -+ * mode is on and determine the sequence of hdmi bind. -+ */ -+ if (device_property_read_bool(dev, "split-mode") || -+ device_property_read_bool(secondary->dev, "split-mode")) { -+ plat_data->split_mode = true; -+ secondary->plat_data->split_mode = true; -+ if (!secondary->plat_data->first_screen) -+ plat_data->first_screen = true; -+ } -+ -+ if (device_property_read_bool(dev, "user-split-mode") || -+ device_property_read_bool(secondary->dev, "user-split-mode")) { -+ hdmi->user_split_mode = true; -+ secondary->user_split_mode = true; -+ } -+ } -+ -+ if (!plat_data->first_screen) { -+ encoder = &hdmi->encoder.encoder; -+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); -+ rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder, dev->of_node, 0, 0); -+ /* -+ * If we failed to find the CRTC(s) which this encoder is -+ * supposed to be connected to, it's because the CRTC has -+ * not been registered yet. Defer probing, and hope that -+ * the required CRTC is added later. -+ */ -+ if (encoder->possible_crtcs == 0) -+ return -EPROBE_DEFER; -+ -+ drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); -+ drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); -+ } -+ -+ if (!plat_data->max_tmdsclk) -+ hdmi->max_tmdsclk = 594000; -+ else -+ hdmi->max_tmdsclk = plat_data->max_tmdsclk; -+ -+ hdmi->is_hdmi_qp = plat_data->is_hdmi_qp; -+ -+ hdmi->unsupported_yuv_input = plat_data->unsupported_yuv_input; -+ hdmi->unsupported_deep_color = plat_data->unsupported_deep_color; - - ret = rockchip_hdmi_parse_dt(hdmi); - if (ret) { -@@ -596,34 +2944,44 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, - return ret; - } - -- hdmi->phy = devm_phy_optional_get(dev, "hdmi"); -- if (IS_ERR(hdmi->phy)) { -- ret = PTR_ERR(hdmi->phy); -- if (ret != -EPROBE_DEFER) -- DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); -+ ret = clk_prepare_enable(hdmi->aud_clk); -+ if (ret) { -+ dev_err(hdmi->dev, "Failed to enable HDMI aud_clk: %d\n", ret); -+ return ret; -+ } -+ -+ ret = clk_prepare_enable(hdmi->hpd_clk); -+ if (ret) { -+ dev_err(hdmi->dev, "Failed to enable HDMI hpd_clk: %d\n", ret); - return ret; - } - -- ret = regulator_enable(hdmi->avdd_0v9); -+ ret = clk_prepare_enable(hdmi->hclk_vo1); - if (ret) { -- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret); -- goto err_avdd_0v9; -+ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vo1: %d\n", ret); -+ return ret; - } - -- ret = regulator_enable(hdmi->avdd_1v8); -+ ret = clk_prepare_enable(hdmi->earc_clk); - if (ret) { -- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret); -- goto err_avdd_1v8; -+ dev_err(hdmi->dev, "Failed to enable HDMI earc_clk: %d\n", ret); -+ return ret; - } - -- ret = clk_prepare_enable(hdmi->ref_clk); -+ ret = clk_prepare_enable(hdmi->hdmitx_ref); - if (ret) { -- DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n", -- ret); -- goto err_clk; -+ dev_err(hdmi->dev, "Failed to enable HDMI hdmitx_ref: %d\n", -+ ret); -+ return ret; -+ } -+ -+ ret = clk_prepare_enable(hdmi->pclk); -+ if (ret) { -+ dev_err(hdmi->dev, "Failed to enable HDMI pclk: %d\n", ret); -+ return ret; - } - -- if (hdmi->chip_data == &rk3568_chip_data) { -+ if (hdmi->chip_data->ddc_en_reg == RK3568_GRF_VO_CON1) { - regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1, - HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK | - RK3568_HDMI_SCLIN_MSK, -@@ -631,32 +2989,112 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, - RK3568_HDMI_SCLIN_MSK)); - } - -- drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); -- drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); -+ if (hdmi->is_hdmi_qp) { -+ if (!hdmi->id) { -+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | -+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | -+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | -+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); -+ -+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, -+ RK3588_SET_HPD_PATH_MASK); -+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); -+ -+ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, -+ RK3588_HDMI0_GRANT_SEL); -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); -+ } else { -+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | -+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | -+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | -+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); -+ -+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, -+ RK3588_SET_HPD_PATH_MASK); -+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); -+ -+ val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, -+ RK3588_HDMI1_GRANT_SEL); -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); -+ } -+ init_hpd_work(hdmi); -+ } - -- platform_set_drvdata(pdev, hdmi); -+ ret = clk_prepare_enable(hdmi->phyref_clk); -+ if (ret) { -+ DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n", -+ ret); -+ return ret; -+ } - -- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data); -+ ret = clk_prepare_enable(hdmi->hclk_vio); -+ if (ret) { -+ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vio: %d\n", -+ ret); -+ return ret; -+ } - -- /* -- * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), -- * which would have called the encoder cleanup. Do it manually. -- */ -- if (IS_ERR(hdmi->hdmi)) { -- ret = PTR_ERR(hdmi->hdmi); -- goto err_bind; -+ ret = clk_prepare_enable(hdmi->hclk_vop); -+ if (ret) { -+ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vop: %d\n", -+ ret); -+ return ret; - } - -- return 0; -+ if (!hdmi->id) -+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK); -+ else -+ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK); -+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); -+ -+ if (hdmi->is_hdmi_qp) { -+ hdmi->hpd_irq = platform_get_irq(pdev, 4); -+ if (hdmi->hpd_irq < 0) -+ return hdmi->hpd_irq; -+ -+ ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq, -+ rockchip_hdmi_hardirq, -+ rockchip_hdmi_irq, -+ IRQF_SHARED, "dw-hdmi-qp-hpd", -+ hdmi); -+ if (ret) -+ return ret; -+ } -+ -+ hdmi->phy = devm_phy_optional_get(dev, "hdmi"); -+ if (IS_ERR(hdmi->phy)) { -+ hdmi->phy = devm_phy_optional_get(dev, "hdmi_phy"); -+ if (IS_ERR(hdmi->phy)) { -+ ret = PTR_ERR(hdmi->phy); -+ if (ret != -EPROBE_DEFER) -+ DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); -+ return ret; -+ } -+ } -+ -+ if (hdmi->is_hdmi_qp) { -+ hdmi->hdmi_qp = dw_hdmi_qp_bind(pdev, &hdmi->encoder.encoder, plat_data); -+ -+ if (IS_ERR(hdmi->hdmi_qp)) { -+ ret = PTR_ERR(hdmi->hdmi_qp); -+ drm_encoder_cleanup(&hdmi->encoder.encoder); -+ } -+ -+ if (plat_data->split_mode && secondary) { -+ if (device_property_read_bool(dev, "split-mode")) { -+ plat_data->right = secondary->hdmi_qp; -+ secondary->plat_data->left = hdmi->hdmi_qp; -+ } else { -+ plat_data->left = secondary->hdmi_qp; -+ secondary->plat_data->right = hdmi->hdmi_qp; -+ } -+ } -+ -+ return ret; -+ } - --err_bind: -- drm_encoder_cleanup(encoder); -- clk_disable_unprepare(hdmi->ref_clk); --err_clk: -- regulator_disable(hdmi->avdd_1v8); --err_avdd_1v8: -- regulator_disable(hdmi->avdd_0v9); --err_avdd_0v9: - return ret; - } - -@@ -665,12 +3103,24 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, - { - struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); - -- dw_hdmi_unbind(hdmi->hdmi); -- drm_encoder_cleanup(&hdmi->encoder.encoder); -- clk_disable_unprepare(hdmi->ref_clk); -+ if (hdmi->is_hdmi_qp) { -+ cancel_delayed_work(&hdmi->work); -+ flush_workqueue(hdmi->workqueue); -+ destroy_workqueue(hdmi->workqueue); -+ } - -- regulator_disable(hdmi->avdd_1v8); -- regulator_disable(hdmi->avdd_0v9); -+ if (hdmi->is_hdmi_qp) -+ dw_hdmi_qp_unbind(hdmi->hdmi_qp); -+ else -+ dw_hdmi_unbind(hdmi->hdmi); -+ clk_disable_unprepare(hdmi->aud_clk); -+ clk_disable_unprepare(hdmi->phyref_clk); -+ clk_disable_unprepare(hdmi->hclk_vop); -+ clk_disable_unprepare(hdmi->hpd_clk); -+ clk_disable_unprepare(hdmi->hclk_vo1); -+ clk_disable_unprepare(hdmi->earc_clk); -+ clk_disable_unprepare(hdmi->hdmitx_ref); -+ clk_disable_unprepare(hdmi->pclk); - } - - static const struct component_ops dw_hdmi_rockchip_ops = { -@@ -680,30 +3130,132 @@ static const struct component_ops dw_hdmi_rockchip_ops = { - - static int dw_hdmi_rockchip_probe(struct platform_device *pdev) - { -+ struct rockchip_hdmi *hdmi; -+ const struct of_device_id *match; -+ struct dw_hdmi_plat_data *plat_data; -+ int id; -+ -+ hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); -+ if (!hdmi) -+ return -ENOMEM; -+ -+ id = of_alias_get_id(pdev->dev.of_node, "hdmi"); -+ if (id < 0) -+ id = 0; -+ -+ hdmi->id = id; -+ hdmi->dev = &pdev->dev; -+ -+ match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); -+ plat_data = devm_kmemdup(&pdev->dev, match->data, -+ sizeof(*plat_data), GFP_KERNEL); -+ if (!plat_data) -+ return -ENOMEM; -+ -+ plat_data->id = hdmi->id; -+ hdmi->plat_data = plat_data; -+ hdmi->chip_data = plat_data->phy_data; -+ -+ platform_set_drvdata(pdev, hdmi); -+ pm_runtime_enable(&pdev->dev); -+ pm_runtime_get_sync(&pdev->dev); -+ - return component_add(&pdev->dev, &dw_hdmi_rockchip_ops); - } - --static void dw_hdmi_rockchip_remove(struct platform_device *pdev) -+static void dw_hdmi_rockchip_shutdown(struct platform_device *pdev) -+{ -+ struct rockchip_hdmi *hdmi = dev_get_drvdata(&pdev->dev); -+ -+ if (!hdmi) -+ return; -+ -+ if (hdmi->is_hdmi_qp) { -+ cancel_delayed_work(&hdmi->work); -+ flush_workqueue(hdmi->workqueue); -+ dw_hdmi_qp_suspend(hdmi->dev, hdmi->hdmi_qp); -+ } else { -+ dw_hdmi_suspend(hdmi->hdmi); -+ } -+ pm_runtime_put_sync(&pdev->dev); -+} -+ -+static int dw_hdmi_rockchip_remove(struct platform_device *pdev) - { - component_del(&pdev->dev, &dw_hdmi_rockchip_ops); -+ pm_runtime_disable(&pdev->dev); -+ -+ return 0; -+} -+ -+static int dw_hdmi_rockchip_suspend(struct device *dev) -+{ -+ struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); -+ -+ if (hdmi->is_hdmi_qp) -+ dw_hdmi_qp_suspend(dev, hdmi->hdmi_qp); -+ else -+ dw_hdmi_suspend(hdmi->hdmi); -+ pm_runtime_put_sync(dev); -+ -+ return 0; - } - --static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev) -+static int dw_hdmi_rockchip_resume(struct device *dev) - { - struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); -+ u32 val; - -- dw_hdmi_resume(hdmi->hdmi); -+ if (hdmi->is_hdmi_qp) { -+ if (!hdmi->id) { -+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | -+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | -+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | -+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); -+ -+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, -+ RK3588_SET_HPD_PATH_MASK); -+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); -+ -+ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, -+ RK3588_HDMI0_GRANT_SEL); -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); -+ } else { -+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | -+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | -+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | -+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); -+ -+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, -+ RK3588_SET_HPD_PATH_MASK); -+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); -+ -+ val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, -+ RK3588_HDMI1_GRANT_SEL); -+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); -+ } -+ -+ dw_hdmi_qp_resume(dev, hdmi->hdmi_qp); -+ drm_helper_hpd_irq_event(hdmi->drm_dev); -+ } else { -+ dw_hdmi_resume(hdmi->hdmi); -+ } -+ pm_runtime_get_sync(dev); - - return 0; - } - - static const struct dev_pm_ops dw_hdmi_rockchip_pm = { -- SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume) -+ SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_rockchip_suspend, -+ dw_hdmi_rockchip_resume) - }; - - struct platform_driver dw_hdmi_rockchip_pltfm_driver = { - .probe = dw_hdmi_rockchip_probe, -- .remove_new = dw_hdmi_rockchip_remove, -+ .remove = dw_hdmi_rockchip_remove, -+ .shutdown = dw_hdmi_rockchip_shutdown, - .driver = { - .name = "dwhdmi-rockchip", - .pm = &dw_hdmi_rockchip_pm, -diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h -index 6a46baa0737c..67eb3c113cf2 100644 ---- a/include/drm/bridge/dw_hdmi.h -+++ b/include/drm/bridge/dw_hdmi.h -@@ -6,12 +6,15 @@ - #ifndef __DW_HDMI__ - #define __DW_HDMI__ - -+#include - #include -+#include - - struct drm_display_info; - struct drm_display_mode; - struct drm_encoder; - struct dw_hdmi; -+struct dw_hdmi_qp; - struct platform_device; - - /** -@@ -92,6 +95,13 @@ enum dw_hdmi_phy_type { - DW_HDMI_PHY_VENDOR_PHY = 0xfe, - }; - -+struct dw_hdmi_audio_tmds_n { -+ unsigned long tmds; -+ unsigned int n_32k; -+ unsigned int n_44k1; -+ unsigned int n_48k; -+}; -+ - struct dw_hdmi_mpll_config { - unsigned long mpixelclock; - struct { -@@ -112,6 +122,15 @@ struct dw_hdmi_phy_config { - u16 vlev_ctr; /* voltage level control */ - }; - -+struct dw_hdmi_link_config { -+ bool dsc_mode; -+ bool frl_mode; -+ int frl_lanes; -+ int rate_per_lane; -+ int hcactive; -+ u8 pps_payload[128]; -+}; -+ - struct dw_hdmi_phy_ops { - int (*init)(struct dw_hdmi *hdmi, void *data, - const struct drm_display_info *display, -@@ -123,14 +142,51 @@ struct dw_hdmi_phy_ops { - void (*setup_hpd)(struct dw_hdmi *hdmi, void *data); - }; - -+struct dw_hdmi_qp_phy_ops { -+ int (*init)(struct dw_hdmi_qp *hdmi, void *data, -+ struct drm_display_mode *mode); -+ void (*disable)(struct dw_hdmi_qp *hdmi, void *data); -+ enum drm_connector_status (*read_hpd)(struct dw_hdmi_qp *hdmi, -+ void *data); -+ void (*update_hpd)(struct dw_hdmi_qp *hdmi, void *data, -+ bool force, bool disabled, bool rxsense); -+ void (*setup_hpd)(struct dw_hdmi_qp *hdmi, void *data); -+ void (*set_mode)(struct dw_hdmi_qp *dw_hdmi, void *data, -+ u32 mode_mask, bool enable); -+}; -+ -+struct dw_hdmi_property_ops { -+ void (*attach_properties)(struct drm_connector *connector, -+ unsigned int color, int version, -+ void *data); -+ void (*destroy_properties)(struct drm_connector *connector, -+ void *data); -+ int (*set_property)(struct drm_connector *connector, -+ struct drm_connector_state *state, -+ struct drm_property *property, -+ u64 val, -+ void *data); -+ int (*get_property)(struct drm_connector *connector, -+ const struct drm_connector_state *state, -+ struct drm_property *property, -+ u64 *val, -+ void *data); -+}; -+ - struct dw_hdmi_plat_data { - struct regmap *regm; - - unsigned int output_port; -- -+ unsigned int disable_cec; -+ unsigned long input_bus_format; - unsigned long input_bus_encoding; -+ unsigned int max_tmdsclk; -+ int id; - bool use_drm_infoframe; - bool ycbcr_420_allowed; -+ bool unsupported_yuv_input; -+ bool unsupported_deep_color; -+ bool is_hdmi_qp; - - /* - * Private data passed to all the .mode_valid() and .configure_phy() -@@ -139,29 +195,55 @@ struct dw_hdmi_plat_data { - void *priv_data; - - /* Platform-specific mode validation (optional). */ -- enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi, void *data, -+ enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi, -+ void *data, - const struct drm_display_info *info, - const struct drm_display_mode *mode); - -- /* Platform-specific audio enable/disable (optional) */ -- void (*enable_audio)(struct dw_hdmi *hdmi, int channel, -- int width, int rate, int non_pcm); -- void (*disable_audio)(struct dw_hdmi *hdmi); -- - /* Vendor PHY support */ - const struct dw_hdmi_phy_ops *phy_ops; -+ const struct dw_hdmi_qp_phy_ops *qp_phy_ops; - const char *phy_name; - void *phy_data; - unsigned int phy_force_vendor; -+ const struct dw_hdmi_audio_tmds_n *tmds_n_table; -+ -+ /* split mode */ -+ bool split_mode; -+ bool first_screen; -+ struct dw_hdmi_qp *left; -+ struct dw_hdmi_qp *right; - - /* Synopsys PHY support */ - const struct dw_hdmi_mpll_config *mpll_cfg; -+ const struct dw_hdmi_mpll_config *mpll_cfg_420; - const struct dw_hdmi_curr_ctrl *cur_ctr; - const struct dw_hdmi_phy_config *phy_config; - int (*configure_phy)(struct dw_hdmi *hdmi, void *data, - unsigned long mpixelclock); - -- unsigned int disable_cec : 1; -+ unsigned long (*get_input_bus_format)(void *data); -+ unsigned long (*get_output_bus_format)(void *data); -+ unsigned long (*get_enc_in_encoding)(void *data); -+ unsigned long (*get_enc_out_encoding)(void *data); -+ unsigned long (*get_quant_range)(void *data); -+ struct drm_property *(*get_hdr_property)(void *data); -+ struct drm_property_blob *(*get_hdr_blob)(void *data); -+ bool (*get_color_changed)(void *data); -+ int (*get_yuv422_format)(struct drm_connector *connector, -+ struct edid *edid); -+ int (*get_edid_dsc_info)(void *data, struct edid *edid); -+ int (*get_next_hdr_data)(void *data, struct edid *edid, -+ struct drm_connector *connector); -+ struct dw_hdmi_link_config *(*get_link_cfg)(void *data); -+ void (*set_grf_cfg)(void *data); -+ void (*convert_to_split_mode)(struct drm_display_mode *mode); -+ void (*convert_to_origin_mode)(struct drm_display_mode *mode); -+ int (*dclk_set)(void *data, bool enable); -+ -+ /* Vendor Property support */ -+ const struct dw_hdmi_property_ops *property_ops; -+ struct drm_connector *connector; - }; - - struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, -@@ -170,16 +252,15 @@ void dw_hdmi_remove(struct dw_hdmi *hdmi); - void dw_hdmi_unbind(struct dw_hdmi *hdmi); - struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, - struct drm_encoder *encoder, -- const struct dw_hdmi_plat_data *plat_data); -+ struct dw_hdmi_plat_data *plat_data); - -+void dw_hdmi_suspend(struct dw_hdmi *hdmi); - void dw_hdmi_resume(struct dw_hdmi *hdmi); - - void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense); - - int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn, - struct device *codec_dev); --void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm); --void dw_hdmi_set_sample_width(struct dw_hdmi *hdmi, unsigned int width); - void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); - void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt); - void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi, u8 *channel_status); -@@ -205,6 +286,32 @@ enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, - void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, - bool force, bool disabled, bool rxsense); - void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data); -+void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi); -+void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val); -+bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi); -+int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi); -+void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap); -+ -+void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi); -+struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, -+ struct drm_encoder *encoder, -+ struct dw_hdmi_plat_data *plat_data); -+void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi); -+void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi); -+void dw_hdmi_qp_cec_set_hpd(struct dw_hdmi_qp *hdmi, bool plug_in, bool change); -+void dw_hdmi_qp_set_cec_adap(struct dw_hdmi_qp *hdmi, struct cec_adapter *adap); -+int dw_hdmi_qp_set_earc(struct dw_hdmi_qp *hdmi); -+void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned int rate); -+void dw_hdmi_qp_set_channel_count(struct dw_hdmi_qp *hdmi, unsigned int cnt); -+void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, u8 *channel_status, -+ bool ref2stream); -+void dw_hdmi_qp_set_channel_allocation(struct dw_hdmi_qp *hdmi, unsigned int ca); -+void dw_hdmi_qp_set_audio_infoframe(struct dw_hdmi_qp *hdmi, -+ struct hdmi_codec_params *hparms); -+void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi); -+void dw_hdmi_qp_audio_disable(struct dw_hdmi_qp *hdmi); -+int dw_hdmi_qp_set_plugged_cb(struct dw_hdmi_qp *hdmi, hdmi_codec_plugged_cb fn, -+ struct device *codec_dev); - - bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi); - --- -Armbian - diff --git a/patch/kernel/rockchip-rk3588-edge/0029-Add-VOP2-Support-for-RK3588.patch b/patch/kernel/rockchip-rk3588-edge/0029-Add-VOP2-Support-for-RK3588.patch deleted file mode 100644 index 61d74e4add..0000000000 --- a/patch/kernel/rockchip-rk3588-edge/0029-Add-VOP2-Support-for-RK3588.patch +++ /dev/null @@ -1,833 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Andy Yan -Date: Sat, 4 Nov 2023 17:54:08 +0800 -Subject: drm/rockchip: vop2: Add cursor plane support - -Signed-off-by: Andy Yan ---- - drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 88 ++++++++-- - 1 file changed, 73 insertions(+), 15 deletions(-) - -diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c -index fdd768bbd487..892b8ab02cc3 100644 ---- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c -+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c -@@ -169,6 +169,7 @@ struct vop2_video_port { - u32 win_mask; - - struct vop2_win *primary_plane; -+ struct vop2_win *cursor_plane; - struct drm_pending_vblank_event *event; - - unsigned int nlayers; -@@ -2683,12 +2684,63 @@ static struct vop2_video_port *find_vp_without_primary(struct vop2 *vop2) - return NULL; - } - -+static struct vop2_video_port *find_vp_without_cursor(struct vop2 *vop2) -+{ -+ int i; -+ -+ for (i = 0; i < vop2->data->nr_vps; i++) { -+ struct vop2_video_port *vp = &vop2->vps[i]; -+ -+ if (!vp->crtc.port) -+ continue; -+ /* -+ * Only assign a cursor plane for a VP if it has more than 2 layers -+ */ -+ if (vp->nlayers <= 2) -+ continue; -+ if (vp->cursor_plane) -+ continue; -+ -+ return vp; -+ } -+ -+ return NULL; -+} -+ -+/* -+ * divide the total windows equally between all used vp -+ */ -+static void vop2_calc_layers_for_each_vp(struct vop2 *vop2, int nvps) -+{ -+ const struct vop2_data *vop2_data = vop2->data; -+ struct vop2_video_port *vp; -+ unsigned int nlayers = vop2_data->win_size / nvps; -+ unsigned int used_layers = 0; -+ int i; -+ -+ for (i = 0; i < vop2->data->nr_vps; i++) { -+ vp = &vop2->vps[i]; -+ -+ if (!vp->crtc.port) -+ continue; -+ /* -+ * The last VP maybe get a fewer windows -+ */ -+ if (vop2_data->win_size - used_layers < nlayers) -+ vp->nlayers = vop2_data->win_size - used_layers; -+ else -+ vp->nlayers = nlayers; -+ used_layers += vp->nlayers; -+ } -+} -+ - static int vop2_create_crtcs(struct vop2 *vop2) - { - const struct vop2_data *vop2_data = vop2->data; - struct drm_device *drm = vop2->drm; - struct device *dev = vop2->dev; -- struct drm_plane *plane; -+ struct drm_plane *primary; -+ struct drm_plane *cursor; - struct device_node *port; - struct vop2_video_port *vp; - int i, nvp, nvps = 0; -@@ -2729,6 +2781,8 @@ static int vop2_create_crtcs(struct vop2 *vop2) - nvps++; - } - -+ vop2_calc_layers_for_each_vp(vop2, nvps); -+ - nvp = 0; - for (i = 0; i < vop2->registered_num_wins; i++) { - struct vop2_win *win = &vop2->win[i]; -@@ -2760,6 +2814,18 @@ static int vop2_create_crtcs(struct vop2 *vop2) - } - } - -+ if (win->type == DRM_PLANE_TYPE_OVERLAY) { -+ vp = find_vp_without_cursor(vop2); -+ if (vp) { -+ win->type = DRM_PLANE_TYPE_CURSOR; -+ /* -+ * let __drm_crtc_init_with_planes handle it -+ */ -+ possible_crtcs = 0; -+ vp->cursor_plane = win; -+ } -+ } -+ - if (win->type == DRM_PLANE_TYPE_OVERLAY) - possible_crtcs = (1 << nvps) - 1; - -@@ -2777,9 +2843,13 @@ static int vop2_create_crtcs(struct vop2 *vop2) - if (!vp->crtc.port) - continue; - -- plane = &vp->primary_plane->base; -+ primary = &vp->primary_plane->base; -+ if (vp->cursor_plane) -+ cursor = &vp->cursor_plane->base; -+ else -+ cursor = NULL; - -- ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL, -+ ret = drm_crtc_init_with_planes(drm, &vp->crtc, primary, cursor, - &vop2_crtc_funcs, - "video_port%d", vp->id); - if (ret) { -@@ -2792,18 +2862,6 @@ static int vop2_create_crtcs(struct vop2 *vop2) - init_completion(&vp->dsp_hold_completion); - } - -- /* -- * On the VOP2 it's very hard to change the number of layers on a VP -- * during runtime, so we distribute the layers equally over the used -- * VPs -- */ -- for (i = 0; i < vop2->data->nr_vps; i++) { -- struct vop2_video_port *vp = &vop2->vps[i]; -- -- if (vp->crtc.port) -- vp->nlayers = vop2_data->win_size / nvps; -- } -- - return 0; - } - --- -Armbian - -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Andy Yan -Date: Fri, 10 Nov 2023 08:47:21 +0800 -Subject: drm/rockchip: vop2: A workaround for cursor plane zpos - -Signed-off-by: Andy Yan ---- - drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c -index 892b8ab02cc3..ae755ecb991e 100644 ---- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c -+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c -@@ -2640,8 +2640,11 @@ static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win, - unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) | - BIT(DRM_MODE_BLEND_PREMULTI) | - BIT(DRM_MODE_BLEND_COVERAGE); -+ unsigned int zpos = win->win_id; - int ret; - -+ if (win->type == DRM_PLANE_TYPE_CURSOR) -+ zpos = 8; - ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs, - &vop2_plane_funcs, win_data->formats, - win_data->nformats, -@@ -2660,7 +2663,7 @@ static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win, - win->data->supported_rotations); - drm_plane_create_alpha_property(&win->base); - drm_plane_create_blend_mode_property(&win->base, blend_caps); -- drm_plane_create_zpos_property(&win->base, win->win_id, 0, -+ drm_plane_create_zpos_property(&win->base, zpos, 0, - vop2->registered_num_wins - 1); - - return 0; --- -Armbian - -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Andy Yan -Date: Tue, 7 Nov 2023 15:59:00 +0800 -Subject: drm/rockchip: vop2: Add debugfs support - -/sys/kernel/debug/dri/vop2/summary: dump vop display state -/sys/kernel/debug/dri/vop2/regs: dump whole vop registers -/sys/kernel/debug/dri/vop2/active_regs: only dump the registers of -actived modules - -Signed-off-by: Andy Yan ---- - drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 398 ++++++++++ - 1 file changed, 398 insertions(+) - -diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c -index ae755ecb991e..c4e588f234c6 100644 ---- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c -+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c -@@ -27,6 +27,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -187,6 +188,7 @@ struct vop2 { - */ - u32 registered_num_wins; - -+ struct resource *res; - void __iomem *regs; - struct regmap *map; - -@@ -238,6 +240,44 @@ struct vop2 { - - #define vop2_output_if_is_dpi(x) ((x) == ROCKCHIP_VOP2_EP_RGB0) - -+struct vop2_regs_dump { -+ const char *name; -+ u32 base; -+ u32 en_reg; -+ u32 en_val; -+ u32 en_mask; -+}; -+ -+/* -+ * bus-format types. -+ */ -+struct drm_bus_format_enum_list { -+ int type; -+ const char *name; -+}; -+ -+static const struct drm_bus_format_enum_list drm_bus_format_enum_list[] = { -+ { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, -+ { MEDIA_BUS_FMT_RGB565_1X16, "RGB565_1X16" }, -+ { MEDIA_BUS_FMT_RGB666_1X18, "RGB666_1X18" }, -+ { MEDIA_BUS_FMT_RGB666_1X24_CPADHI, "RGB666_1X24_CPADHI" }, -+ { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, "RGB666_1X7X3_SPWG" }, -+ { MEDIA_BUS_FMT_YUV8_1X24, "YUV8_1X24" }, -+ { MEDIA_BUS_FMT_UYYVYY8_0_5X24, "UYYVYY8_0_5X24" }, -+ { MEDIA_BUS_FMT_YUV10_1X30, "YUV10_1X30" }, -+ { MEDIA_BUS_FMT_UYYVYY10_0_5X30, "UYYVYY10_0_5X30" }, -+ { MEDIA_BUS_FMT_RGB888_3X8, "RGB888_3X8" }, -+ { MEDIA_BUS_FMT_RGB888_1X24, "RGB888_1X24" }, -+ { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, "RGB888_1X7X4_SPWG" }, -+ { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, "RGB888_1X7X4_JEIDA" }, -+ { MEDIA_BUS_FMT_UYVY8_2X8, "UYVY8_2X8" }, -+ { MEDIA_BUS_FMT_YUYV8_1X16, "YUYV8_1X16" }, -+ { MEDIA_BUS_FMT_UYVY8_1X16, "UYVY8_1X16" }, -+ { MEDIA_BUS_FMT_RGB101010_1X30, "RGB101010_1X30" }, -+ { MEDIA_BUS_FMT_YUYV10_1X20, "YUYV10_1X20" }, -+}; -+static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list) -+ - static const struct regmap_config vop2_regmap_config; - - static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc) -@@ -2512,6 +2552,362 @@ static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = { - .atomic_disable = vop2_crtc_atomic_disable, - }; - -+static void vop2_dump_connector_on_crtc(struct drm_crtc *crtc, struct seq_file *s) -+{ -+ struct drm_connector_list_iter conn_iter; -+ struct drm_connector *connector; -+ -+ drm_connector_list_iter_begin(crtc->dev, &conn_iter); -+ drm_for_each_connector_iter(connector, &conn_iter) { -+ if (crtc->state->connector_mask & drm_connector_mask(connector)) -+ seq_printf(s, " Connector: %s\n", connector->name); -+ -+ } -+ drm_connector_list_iter_end(&conn_iter); -+} -+ -+static int vop2_plane_state_dump(struct seq_file *s, struct drm_plane *plane) -+{ -+ struct vop2_win *win = to_vop2_win(plane); -+ struct drm_plane_state *pstate = plane->state; -+ struct drm_rect *src, *dst; -+ struct drm_framebuffer *fb;; -+ struct drm_gem_object *obj; -+ struct rockchip_gem_object *rk_obj; -+ bool xmirror; -+ bool ymirror; -+ bool rotate_270; -+ bool rotate_90; -+ dma_addr_t fb_addr; -+ int i; -+ -+ seq_printf(s, " %s: %s\n", win->data->name, pstate->crtc ? "ACTIVE" : "DISABLED"); -+ if (!pstate || !pstate->fb) -+ return 0; -+ -+ fb = pstate->fb; -+ src = &pstate->src; -+ dst = &pstate->dst; -+ xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false; -+ ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false; -+ rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270; -+ rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90; -+ -+ seq_printf(s, "\twin_id: %d\n", win->win_id); -+ -+ seq_printf(s, "\tformat: %p4cc%s glb_alpha[0x%x]\n", -+ &fb->format->format, -+ drm_is_afbc(fb->modifier) ? "[AFBC]" : "", -+ pstate->alpha >> 8); -+ seq_printf(s, "\trotate: xmirror: %d ymirror: %d rotate_90: %d rotate_270: %d\n", -+ xmirror, ymirror, rotate_90, rotate_270); -+ seq_printf(s, "\tzpos: %d\n", pstate->normalized_zpos); -+ seq_printf(s, "\tsrc: pos[%d, %d] rect[%d x %d]\n", src->x1 >> 16, -+ src->y1 >> 16, drm_rect_width(src) >> 16, -+ drm_rect_height(src) >> 16); -+ seq_printf(s, "\tdst: pos[%d, %d] rect[%d x %d]\n", dst->x1, dst->y1, -+ drm_rect_width(dst), drm_rect_height(dst)); -+ -+ for (i = 0; i < fb->format->num_planes; i++) { -+ obj = fb->obj[0]; -+ rk_obj = to_rockchip_obj(obj); -+ fb_addr = rk_obj->dma_addr + fb->offsets[0]; -+ -+ seq_printf(s, "\tbuf[%d]: addr: %pad pitch: %d offset: %d\n", -+ i, &fb_addr, fb->pitches[i], fb->offsets[i]); -+ } -+ -+ return 0; -+} -+ -+static int vop2_crtc_state_dump(struct drm_crtc *crtc, struct seq_file *s) -+{ -+ struct vop2_video_port *vp = to_vop2_video_port(crtc); -+ struct drm_crtc_state *cstate = crtc->state; -+ struct rockchip_crtc_state *vcstate; -+ struct drm_display_mode *mode; -+ struct drm_plane *plane; -+ bool interlaced; -+ -+ seq_printf(s, "Video Port%d: %s\n", vp->id, !cstate ? "DISABLED": cstate->active ? "ACTIVE" : "DISABLED"); -+ -+ if (!cstate || !cstate->active) -+ return 0; -+ -+ mode = &crtc->state->adjusted_mode; -+ vcstate = to_rockchip_crtc_state(cstate); -+ interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); -+ -+ vop2_dump_connector_on_crtc(crtc, s); -+ seq_printf(s, "\tbus_format[%x]: %s\n", vcstate->bus_format, -+ drm_get_bus_format_name(vcstate->bus_format)); -+ seq_printf(s, "\toutput_mode[%x]", vcstate->output_mode); -+ seq_printf(s, " color_space[%d]\n", vcstate->color_space); -+ seq_printf(s, " Display mode: %dx%d%s%d\n", -+ mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p", -+ drm_mode_vrefresh(mode)); -+ seq_printf(s, "\tclk[%d] real_clk[%d] type[%x] flag[%x]\n", -+ mode->clock, mode->crtc_clock, mode->type, mode->flags); -+ seq_printf(s, "\tH: %d %d %d %d\n", mode->hdisplay, mode->hsync_start, -+ mode->hsync_end, mode->htotal); -+ seq_printf(s, "\tV: %d %d %d %d\n", mode->vdisplay, mode->vsync_start, -+ mode->vsync_end, mode->vtotal); -+ -+ drm_atomic_crtc_for_each_plane(plane, crtc) { -+ vop2_plane_state_dump(s, plane); -+ } -+ -+ return 0; -+} -+ -+static int vop2_summary_show(struct seq_file *s, void *data) -+{ -+ struct drm_info_node *node = s->private; -+ struct drm_minor *minor = node->minor; -+ struct drm_device *drm_dev = minor->dev; -+ struct drm_crtc *crtc; -+ -+ drm_modeset_lock_all(drm_dev); -+ drm_for_each_crtc(crtc, drm_dev) { -+ vop2_crtc_state_dump(crtc, s); -+ } -+ drm_modeset_unlock_all(drm_dev); -+ -+ return 0; -+} -+ -+static void vop2_regs_print(struct vop2 *vop2, struct seq_file *s, struct vop2_regs_dump *dump) -+{ -+ resource_size_t start; -+ const int reg_num = 0x110 / 4; -+ u32 val; -+ int i; -+ -+ if (dump->en_mask) { -+ val = vop2_readl(vop2, dump->base + dump->en_reg); -+ if ((val & dump->en_mask) != dump->en_val) -+ return; -+ } -+ seq_printf(s, "\n%s:\n", dump->name); -+ -+ start = vop2->res->start + dump->base; -+ for (i = 0; i < reg_num;) { -+ seq_printf(s, "%08x: %08x %08x %08x %08x\n", (u32)start + i * 4, -+ vop2_readl(vop2, dump->base + (4 * i)), -+ vop2_readl(vop2, dump->base + (4 * (i + 1))), -+ vop2_readl(vop2, dump->base + (4 * (i + 2))), -+ vop2_readl(vop2, dump->base + (4 * (i + 3)))); -+ i += 4; -+ } -+ -+} -+ -+static int vop2_regs_show(struct seq_file *s, void *arg) -+{ -+ struct drm_info_node *node = s->private; -+ struct vop2 *vop2 = (struct vop2 *)node->info_ent->data; -+ struct drm_minor *minor = node->minor; -+ struct drm_device *drm_dev = minor->dev; -+ -+ struct vop2_regs_dump dump; -+ -+ drm_modeset_lock_all(drm_dev); -+ -+ if (vop2->enable_count) { -+ dump.en_mask = 0; -+ -+ dump.name = "SYS"; -+ dump.base = RK3568_REG_CFG_DONE; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "OVL"; -+ dump.base = RK3568_OVL_CTRL; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "VP0"; -+ dump.base = 0x0c00; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "VP1"; -+ dump.base = 0x0c00 + 0x100; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "VP2"; -+ dump.base = 0x0c00 + 0x200; -+ vop2_regs_print(vop2, s, &dump); -+ if (vop2->data->soc_id == 3588) { -+ dump.name = "VP3"; -+ dump.base = 0x0c00 + 0x300; -+ vop2_regs_print(vop2, s, &dump); -+ } -+ dump.name = "Cluster0"; -+ dump.base = 0x1000; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "Cluster1"; -+ dump.base = 0x1000 + 0x200; -+ vop2_regs_print(vop2, s, &dump); -+ if (vop2->data->soc_id == 3588) { -+ dump.name = "Cluster2"; -+ dump.base = 0x1000 + 0x400; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "Cluster3"; -+ dump.base = 0x1000 + 0x600; -+ vop2_regs_print(vop2, s, &dump); -+ } -+ dump.name = "Esmart0"; -+ dump.base = 0x1000 + 0x800; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "Esmart1"; -+ dump.base = 0x1000 + 0xa00; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "Esmart2"; -+ if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) -+ dump.name = "Smart0"; -+ dump.base = 0x1000 + 0xc00; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "Esmart3"; -+ if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) -+ dump.name = "Smart1"; -+ dump.base = 0x1000 + 0xe00; -+ vop2_regs_print(vop2, s, &dump); -+ } else { -+ seq_printf(s, "VOP disabled:\n"); -+ } -+ drm_modeset_unlock_all(drm_dev); -+ -+ return 0; -+} -+ -+static int vop2_active_regs_show(struct seq_file *s, void *data) -+{ -+ struct drm_info_node *node = s->private; -+ struct vop2 *vop2 = (struct vop2 *)node->info_ent->data; -+ struct drm_minor *minor = node->minor; -+ struct drm_device *drm_dev = minor->dev; -+ -+ struct vop2_regs_dump dump; -+ -+ drm_modeset_lock_all(drm_dev); -+ if (vop2->enable_count) { -+ dump.en_mask = 0; -+ -+ dump.name = "SYS"; -+ dump.base = RK3568_REG_CFG_DONE; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "OVL"; -+ dump.base = RK3568_OVL_CTRL; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "VP0"; -+ dump.base = 0x0c00; -+ dump.en_mask = RK3568_VP_DSP_CTRL__STANDBY; -+ dump.en_reg = RK3568_VP_DSP_CTRL; -+ dump.en_val = 0; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "VP1"; -+ dump.base = 0x0c00 + 0x100; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "VP2"; -+ dump.base = 0x0c00 + 0x200; -+ vop2_regs_print(vop2, s, &dump); -+ if (vop2->data->soc_id == 3588) { -+ dump.name = "VP3"; -+ dump.base = 0x0c00 + 0x300; -+ vop2_regs_print(vop2, s, &dump); -+ } -+ -+ dump.en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN; -+ dump.en_reg = RK3568_CLUSTER_WIN_CTRL0; -+ dump.en_val = 1; -+ -+ dump.name = "Cluster0"; -+ dump.base = 0x1000; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "Cluster1"; -+ dump.base = 0x1000 + 0x200; -+ vop2_regs_print(vop2, s, &dump); -+ if (vop2->data->soc_id == 3588) { -+ dump.name = "Cluster2"; -+ dump.base = 0x1000 + 0x400; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "Cluster3"; -+ dump.base = 0x1000 + 0x600; -+ vop2_regs_print(vop2, s, &dump); -+ } -+ -+ dump.name = "Esmart0"; -+ dump.base = 0x1000 + 0x800; -+ dump.en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN; -+ dump.en_reg = RK3568_SMART_REGION0_CTRL; -+ dump.en_val = 1; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "Esmart1"; -+ dump.base = 0x1000 + 0xa00; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "Esmart2"; -+ if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) -+ dump.name = "Smart0"; -+ dump.base = 0x1000 + 0xc00; -+ vop2_regs_print(vop2, s, &dump); -+ -+ dump.name = "Esmart3"; -+ if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) -+ dump.name = "Smart1"; -+ dump.base = 0x1000 + 0xe00; -+ vop2_regs_print(vop2, s, &dump); -+ } else { -+ seq_printf(s, "VOP disabled:\n"); -+ } -+ drm_modeset_unlock_all(drm_dev); -+ -+ return 0; -+} -+ -+static struct drm_info_list vop2_debugfs_list[] = { -+ { "summary", vop2_summary_show, 0, NULL }, -+ { "active_regs", vop2_active_regs_show, 0, NULL }, -+ { "regs", vop2_regs_show, 0, NULL }, -+}; -+ -+static void vop2_debugfs_init(struct vop2 *vop2, struct drm_minor *minor) -+{ -+ struct dentry *root; -+ unsigned int i; -+ -+ root = debugfs_create_dir("vop2", minor->debugfs_root); -+ if (!IS_ERR(root)) { -+ for (i = 0; i < ARRAY_SIZE(vop2_debugfs_list); i++) -+ vop2_debugfs_list[i].data = vop2; -+ -+ drm_debugfs_create_files(vop2_debugfs_list, -+ ARRAY_SIZE(vop2_debugfs_list), -+ root, minor); -+ } -+} -+ -+static int vop2_crtc_late_register(struct drm_crtc *crtc) -+{ -+ struct vop2_video_port *vp = to_vop2_video_port(crtc); -+ struct vop2 *vop2 = vp->vop2; -+ -+ if (drm_crtc_index(crtc) == 0) -+ vop2_debugfs_init(vop2, crtc->dev->primary); -+ -+ return 0; -+} -+ - static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc) - { - struct rockchip_crtc_state *vcstate; -@@ -2561,6 +2957,7 @@ static const struct drm_crtc_funcs vop2_crtc_funcs = { - .atomic_destroy_state = vop2_crtc_destroy_state, - .enable_vblank = vop2_crtc_enable_vblank, - .disable_vblank = vop2_crtc_disable_vblank, -+ .late_register = vop2_crtc_late_register, - }; - - static irqreturn_t vop2_isr(int irq, void *data) -@@ -3165,6 +3562,7 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) - return -EINVAL; - } - -+ vop2->res = res; - vop2->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(vop2->regs)) - return PTR_ERR(vop2->regs); --- -Armbian - -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Andy Yan -Date: Fri, 10 Nov 2023 19:01:37 +0800 -Subject: arm64: dts: rockchip: Enable hdmi display on rk3588-evb1 - -Signed-off-by: Andy Yan ---- - arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts | 49 ++++++ - arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 74 ++++++++++ - 2 files changed, 123 insertions(+) - -diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts -index 62c05bc68042..e1f801bdf2dd 100644 ---- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts -+++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts -@@ -10,6 +10,7 @@ - #include - #include - #include -+#include - #include "rk3588.dtsi" - - / { -@@ -160,6 +161,17 @@ pcie30_avdd1v8: pcie30-avdd1v8-regulator { - vin-supply = <&avcc_1v8_s0>; - }; - -+ hdmi-con { -+ compatible = "hdmi-connector"; -+ type = "a"; -+ -+ port { -+ hdmi_con_in: endpoint { -+ remote-endpoint = <&hdmi0_out_con>; -+ }; -+ }; -+ }; -+ - vcc12v_dcin: vcc12v-dcin-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc12v_dcin"; -@@ -287,6 +299,43 @@ &cpu_l3 { - mem-supply = <&vdd_cpu_lit_mem_s0>; - }; - -+&hdmi0 { -+ enable-gpios = <&gpio4 RK_PB1 GPIO_ACTIVE_HIGH>; -+ status = "okay"; -+}; -+ -+ -+&hdptxphy_hdmi0 { -+ status = "okay"; -+}; -+ -+&vop_mmu { -+ status = "okay"; -+}; -+ -+&hdmi0_in { -+ hdmi0_in_vp0: endpoint { -+ remote-endpoint = <&vp0_out_hdmi0>; -+ }; -+}; -+ -+&hdmi0_out { -+ hdmi0_out_con: endpoint { -+ remote-endpoint = <&hdmi_con_in>; -+ }; -+}; -+ -+&vop { -+ status = "okay"; -+}; -+ -+&vp0 { -+ vp0_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { -+ reg = ; -+ remote-endpoint = <&hdmi0_in_vp0>; -+ }; -+}; -+ - &gmac0 { - clock_in_out = "output"; - phy-handle = <&rgmii_phy>; -diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -index 7e48d3a16ee0..965d0cb80484 100644 ---- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -@@ -975,6 +975,59 @@ sys_grf: syscon@fd58c000 { - reg = <0x0 0xfd58c000 0x0 0x1000>; - }; - -+ hdptxphy0_grf: syscon@fd5e0000 { -+ compatible = "rockchip,rk3588-hdptxphy-grf", "syscon"; -+ reg = <0x0 0xfd5e0000 0x0 0x100>; -+ }; -+ -+ hdmi0: hdmi@fde80000 { -+ compatible = "rockchip,rk3588-dw-hdmi"; -+ reg = <0x0 0xfde80000 0x0 0x20000>; -+ interrupts = , -+ , -+ , -+ , -+ ; -+ clocks = <&cru PCLK_HDMITX0>, -+ <&cru CLK_HDMIHDP0>, -+ <&cru CLK_HDMITX0_EARC>, -+ <&cru CLK_HDMITX0_REF>, -+ <&cru MCLK_I2S5_8CH_TX>, -+ <&cru HCLK_VO1>; -+ clock-names = "pclk", -+ "hpd", -+ "earc", -+ "hdmitx_ref", -+ "aud", -+ "hclk_vo1"; -+ resets = <&cru SRST_HDMITX0_REF>, <&cru SRST_HDMIHDP0>; -+ reset-names = "ref", "hdp"; -+ power-domains = <&power RK3588_PD_VO1>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&hdmim0_tx0_cec &hdmim0_tx0_hpd &hdmim0_tx0_scl &hdmim0_tx0_sda>; -+ reg-io-width = <4>; -+ rockchip,grf = <&sys_grf>; -+ rockchip,vo1_grf = <&vo1_grf>; -+ phys = <&hdptxphy_hdmi0>; -+ phy-names = "hdmi"; -+ #sound-dai-cells = <0>; -+ status = "disabled"; -+ -+ ports { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ hdmi0_in: port@0 { -+ reg = <0>; -+ }; -+ -+ hdmi0_out: port@1 { -+ reg = <1>; -+ }; -+ -+ }; -+ }; -+ - vop_grf: syscon@fd5a4000 { - compatible = "rockchip,rk3588-vop-grf", "syscon"; - reg = <0x0 0xfd5a4000 0x0 0x2000>; -@@ -2919,6 +2972,27 @@ usbdp_phy0: phy@fed80000 { - status = "disabled"; - }; - -+ hdptxphy_hdmi0: hdmiphy@fed60000 { -+ compatible = "rockchip,rk3588-hdptx-phy-hdmi"; -+ reg = <0x0 0xfed60000 0x0 0x2000>; -+ clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>, <&cru PCLK_HDPTX0>; -+ clock-names = "ref", "apb"; -+ resets = <&cru SRST_HDPTX0>, <&cru SRST_P_HDPTX0>, -+ <&cru SRST_HDPTX0_INIT>, <&cru SRST_HDPTX0_CMN>, -+ <&cru SRST_HDPTX0_LANE>, <&cru SRST_HDPTX0_ROPLL>, -+ <&cru SRST_HDPTX0_LCPLL>; -+ reset-names = "phy", "apb", "init", "cmn", "lane", "ropll", -+ "lcpll"; -+ rockchip,grf = <&hdptxphy0_grf>; -+ #phy-cells = <0>; -+ status = "disabled"; -+ -+ hdptxphy_hdmi_clk0: clk-port { -+ #clock-cells = <0>; -+ status = "disabled"; -+ }; -+ }; -+ - combphy0_ps: phy@fee00000 { - compatible = "rockchip,rk3588-naneng-combphy"; - reg = <0x0 0xfee00000 0x0 0x100>; --- -Armbian - diff --git a/patch/kernel/rockchip-rk3588-edge/0031-Add-HDMI-and-VOP2-to-Rock-5A.patch b/patch/kernel/rockchip-rk3588-edge/0031-Add-HDMI-and-VOP2-to-Rock-5A.patch index e619805535..71caeb1e5e 100644 --- a/patch/kernel/rockchip-rk3588-edge/0031-Add-HDMI-and-VOP2-to-Rock-5A.patch +++ b/patch/kernel/rockchip-rk3588-edge/0031-Add-HDMI-and-VOP2-to-Rock-5A.patch @@ -4,11 +4,11 @@ Date: Tue, 27 Feb 2024 16:04:42 +0300 Subject: Add HDMI and VOP2 to Rock 5A --- - arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts | 48 ++++++++++ - 1 file changed, 48 insertions(+) + arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts | 30 ++++++++++ + 1 file changed, 30 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts -index a9ad9d2d54ae..e62eacbd54f6 100644 +index a9ad9d2d54ae..46cb19e86cc1 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts @@ -5,6 +5,7 @@ @@ -19,31 +19,12 @@ index a9ad9d2d54ae..e62eacbd54f6 100644 #include "rk3588s.dtsi" / { -@@ -56,6 +57,17 @@ fan: pwm-fan { - #cooling-cells = <2>; - }; - -+ hdmi-con { -+ compatible = "hdmi-connector"; -+ type = "a"; -+ -+ port { -+ hdmi_con_in: endpoint { -+ remote-endpoint = <&hdmi0_out_con>; -+ }; -+ }; -+ }; -+ - vcc12v_dcin: vcc12v-dcin-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc12v_dcin"; -@@ -768,3 +780,39 @@ &usb_host1_ohci { +@@ -768,3 +769,32 @@ &usb_host1_ohci { &usb_host2_xhci { status = "okay"; }; + +&hdmi0 { -+ enable-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + @@ -61,12 +42,6 @@ index a9ad9d2d54ae..e62eacbd54f6 100644 + }; +}; + -+&hdmi0_out { -+ hdmi0_out_con: endpoint { -+ remote-endpoint = <&hdmi_con_in>; -+ }; -+}; -+ +&vop { + status = "okay"; +}; diff --git a/patch/kernel/rockchip-rk3588-edge/0035-phy-rockchip-Add-Samsung-HDMI-eDP-Combo-PHY-driver.patch b/patch/kernel/rockchip-rk3588-edge/0035-phy-rockchip-Add-Samsung-HDMI-eDP-Combo-PHY-driver.patch new file mode 100644 index 0000000000..b12c933568 --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0035-phy-rockchip-Add-Samsung-HDMI-eDP-Combo-PHY-driver.patch @@ -0,0 +1,1091 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Wed, 14 Feb 2024 13:45:37 +0200 +Subject: phy: rockchip: Add Samsung HDMI/eDP Combo PHY driver + +Add driver for the HDMI/eDP TX Combo PHY found on Rockchip RK3588 SoC. + +The PHY is based on a Samsung IP block and supports HDMI 2.1 TMDS, FRL +and eDP links. The maximum data rate is 12Gbps (FRL), while the minimum +is 250Mbps (TMDS). + +Only the TMDS link is currently supported. + +Co-developed-by: Algea Cao +Signed-off-by: Algea Cao +Tested-by: Heiko Stuebner +Signed-off-by: Cristian Ciocaltea +--- + drivers/phy/rockchip/Kconfig | 8 + + drivers/phy/rockchip/Makefile | 1 + + drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 1028 ++++++++++ + 3 files changed, 1037 insertions(+) + +diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig +index d21b458c1d18..c3d62243b474 100644 +--- a/drivers/phy/rockchip/Kconfig ++++ b/drivers/phy/rockchip/Kconfig +@@ -83,6 +83,14 @@ config PHY_ROCKCHIP_PCIE + help + Enable this to support the Rockchip PCIe PHY. + ++config PHY_ROCKCHIP_SAMSUNG_HDPTX ++ tristate "Rockchip Samsung HDMI/eDP Combo PHY driver" ++ depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF ++ select GENERIC_PHY ++ help ++ Enable this to support the Rockchip HDMI/eDP Combo PHY ++ with Samsung IP block. ++ + config PHY_ROCKCHIP_SNPS_PCIE3 + tristate "Rockchip Snps PCIe3 PHY Driver" + depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST +diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile +index 25d2e1355db7..010a824e32ce 100644 +--- a/drivers/phy/rockchip/Makefile ++++ b/drivers/phy/rockchip/Makefile +@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o + obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o + obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o + obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o ++obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o + obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o + obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o + obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o +diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +new file mode 100644 +index 000000000000..946c01210ac8 +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +@@ -0,0 +1,1028 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd. ++ * Copyright (c) 2024 Collabora Ltd. ++ * ++ * Author: Algea Cao ++ * Author: Cristian Ciocaltea ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define GRF_HDPTX_CON0 0x00 ++#define HDPTX_I_PLL_EN BIT(7) ++#define HDPTX_I_BIAS_EN BIT(6) ++#define HDPTX_I_BGR_EN BIT(5) ++#define GRF_HDPTX_STATUS 0x80 ++#define HDPTX_O_PLL_LOCK_DONE BIT(3) ++#define HDPTX_O_PHY_CLK_RDY BIT(2) ++#define HDPTX_O_PHY_RDY BIT(1) ++#define HDPTX_O_SB_RDY BIT(0) ++ ++#define HDTPX_REG(_n, _min, _max) \ ++ ( \ ++ BUILD_BUG_ON_ZERO((0x##_n) < (0x##_min)) + \ ++ BUILD_BUG_ON_ZERO((0x##_n) > (0x##_max)) + \ ++ ((0x##_n) * 4) \ ++ ) ++ ++#define CMN_REG(n) HDTPX_REG(n, 0000, 00a7) ++#define SB_REG(n) HDTPX_REG(n, 0100, 0129) ++#define LNTOP_REG(n) HDTPX_REG(n, 0200, 0229) ++#define LANE_REG(n) HDTPX_REG(n, 0300, 062d) ++ ++/* CMN_REG(0008) */ ++#define LCPLL_EN_MASK BIT(6) ++#define LCPLL_LCVCO_MODE_EN_MASK BIT(4) ++/* CMN_REG(001e) */ ++#define LCPLL_PI_EN_MASK BIT(5) ++#define LCPLL_100M_CLK_EN_MASK BIT(0) ++/* CMN_REG(0025) */ ++#define LCPLL_PMS_IQDIV_RSTN BIT(4) ++/* CMN_REG(0028) */ ++#define LCPLL_SDC_FRAC_EN BIT(2) ++#define LCPLL_SDC_FRAC_RSTN BIT(0) ++/* CMN_REG(002d) */ ++#define LCPLL_SDC_N_MASK GENMASK(3, 1) ++/* CMN_REG(002e) */ ++#define LCPLL_SDC_NUMBERATOR_MASK GENMASK(5, 0) ++/* CMN_REG(002f) */ ++#define LCPLL_SDC_DENOMINATOR_MASK GENMASK(7, 2) ++#define LCPLL_SDC_NDIV_RSTN BIT(0) ++/* CMN_REG(003d) */ ++#define ROPLL_LCVCO_EN BIT(4) ++/* CMN_REG(004e) */ ++#define ROPLL_PI_EN BIT(5) ++/* CMN_REG(005c) */ ++#define ROPLL_PMS_IQDIV_RSTN BIT(5) ++/* CMN_REG(005e) */ ++#define ROPLL_SDM_EN_MASK BIT(6) ++#define ROPLL_SDM_FRAC_EN_RBR BIT(3) ++#define ROPLL_SDM_FRAC_EN_HBR BIT(2) ++#define ROPLL_SDM_FRAC_EN_HBR2 BIT(1) ++#define ROPLL_SDM_FRAC_EN_HBR3 BIT(0) ++/* CMN_REG(0064) */ ++#define ROPLL_SDM_NUM_SIGN_RBR_MASK BIT(3) ++/* CMN_REG(0069) */ ++#define ROPLL_SDC_N_RBR_MASK GENMASK(2, 0) ++/* CMN_REG(0074) */ ++#define ROPLL_SDC_NDIV_RSTN BIT(2) ++#define ROPLL_SSC_EN BIT(0) ++/* CMN_REG(0081) */ ++#define OVRD_PLL_CD_CLK_EN BIT(8) ++#define PLL_CD_HSCLK_EAST_EN BIT(0) ++/* CMN_REG(0086) */ ++#define PLL_PCG_POSTDIV_SEL_MASK GENMASK(7, 4) ++#define PLL_PCG_CLK_SEL_MASK GENMASK(3, 1) ++#define PLL_PCG_CLK_EN BIT(0) ++/* CMN_REG(0087) */ ++#define PLL_FRL_MODE_EN BIT(3) ++#define PLL_TX_HS_CLK_EN BIT(2) ++/* CMN_REG(0089) */ ++#define LCPLL_ALONE_MODE BIT(1) ++/* CMN_REG(0097) */ ++#define DIG_CLK_SEL BIT(1) ++#define ROPLL_REF BIT(1) ++#define LCPLL_REF 0 ++/* CMN_REG(0099) */ ++#define CMN_ROPLL_ALONE_MODE BIT(2) ++#define ROPLL_ALONE_MODE BIT(2) ++/* CMN_REG(009a) */ ++#define HS_SPEED_SEL BIT(0) ++#define DIV_10_CLOCK BIT(0) ++/* CMN_REG(009b) */ ++#define IS_SPEED_SEL BIT(4) ++#define LINK_SYMBOL_CLOCK BIT(4) ++#define LINK_SYMBOL_CLOCK1_2 0 ++ ++/* SB_REG(0102) */ ++#define OVRD_SB_RXTERM_EN_MASK BIT(5) ++#define SB_RXTERM_EN_MASK BIT(4) ++#define ANA_SB_RXTERM_OFFSP_MASK GENMASK(3, 0) ++/* SB_REG(0103) */ ++#define ANA_SB_RXTERM_OFFSN_MASK GENMASK(6, 3) ++#define OVRD_SB_RX_RESCAL_DONE_MASK BIT(1) ++#define SB_RX_RESCAL_DONE_MASK BIT(0) ++/* SB_REG(0104) */ ++#define OVRD_SB_EN_MASK BIT(5) ++#define SB_EN_MASK BIT(4) ++/* SB_REG(0105) */ ++#define OVRD_SB_EARC_CMDC_EN_MASK BIT(6) ++#define SB_EARC_CMDC_EN_MASK BIT(5) ++#define ANA_SB_TX_HLVL_PROG_MASK GENMASK(2, 0) ++/* SB_REG(0106) */ ++#define ANA_SB_TX_LLVL_PROG_MASK GENMASK(6, 4) ++/* SB_REG(0109) */ ++#define ANA_SB_DMRX_AFC_DIV_RATIO_MASK GENMASK(2, 0) ++/* SB_REG(010f) */ ++#define OVRD_SB_VREG_EN_MASK BIT(7) ++#define SB_VREG_EN_MASK BIT(6) ++#define OVRD_SB_VREG_LPF_BYPASS_MASK BIT(5) ++#define SB_VREG_LPF_BYPASS_MASK BIT(4) ++#define ANA_SB_VREG_GAIN_CTRL_MASK GENMASK(3, 0) ++/* SB_REG(0110) */ ++#define ANA_SB_VREG_REF_SEL_MASK BIT(0) ++/* SB_REG(0113) */ ++#define SB_RX_RCAL_OPT_CODE_MASK GENMASK(5, 4) ++#define SB_RX_RTERM_CTRL_MASK GENMASK(3, 0) ++/* SB_REG(0114) */ ++#define SB_TG_SB_EN_DELAY_TIME_MASK GENMASK(5, 3) ++#define SB_TG_RXTERM_EN_DELAY_TIME_MASK GENMASK(2, 0) ++/* SB_REG(0115) */ ++#define SB_READY_DELAY_TIME_MASK GENMASK(5, 3) ++#define SB_TG_OSC_EN_DELAY_TIME_MASK GENMASK(2, 0) ++/* SB_REG(0116) */ ++#define AFC_RSTN_DELAY_TIME_MASK GENMASK(6, 4) ++/* SB_REG(0117) */ ++#define FAST_PULSE_TIME_MASK GENMASK(3, 0) ++/* SB_REG(011b) */ ++#define SB_EARC_SIG_DET_BYPASS_MASK BIT(4) ++#define SB_AFC_TOL_MASK GENMASK(3, 0) ++/* SB_REG(011f) */ ++#define SB_PWM_AFC_CTRL_MASK GENMASK(7, 2) ++#define SB_RCAL_RSTN_MASK BIT(1) ++/* SB_REG(0120) */ ++#define SB_EARC_EN_MASK BIT(1) ++#define SB_EARC_AFC_EN_MASK BIT(2) ++/* SB_REG(0123) */ ++#define OVRD_SB_READY_MASK BIT(5) ++#define SB_READY_MASK BIT(4) ++ ++/* LNTOP_REG(0200) */ ++#define PROTOCOL_SEL BIT(2) ++#define HDMI_MODE BIT(2) ++#define HDMI_TMDS_FRL_SEL BIT(1) ++/* LNTOP_REG(0206) */ ++#define DATA_BUS_SEL BIT(0) ++#define DATA_BUS_36_40 BIT(0) ++/* LNTOP_REG(0207) */ ++#define LANE_EN 0xf ++#define ALL_LANE_EN 0xf ++ ++/* LANE_REG(0312) */ ++#define LN0_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN0_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN0_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN0_TX_SER_RATE_SEL_HBR3 BIT(2) ++/* LANE_REG(0412) */ ++#define LN1_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN1_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN1_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN1_TX_SER_RATE_SEL_HBR3 BIT(2) ++/* LANE_REG(0512) */ ++#define LN2_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN2_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN2_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN2_TX_SER_RATE_SEL_HBR3 BIT(2) ++/* LANE_REG(0612) */ ++#define LN3_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN3_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN3_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN3_TX_SER_RATE_SEL_HBR3 BIT(2) ++ ++struct lcpll_config { ++ u32 bit_rate; ++ u8 lcvco_mode_en; ++ u8 pi_en; ++ u8 clk_en_100m; ++ u8 pms_mdiv; ++ u8 pms_mdiv_afc; ++ u8 pms_pdiv; ++ u8 pms_refdiv; ++ u8 pms_sdiv; ++ u8 pi_cdiv_rstn; ++ u8 pi_cdiv_sel; ++ u8 sdm_en; ++ u8 sdm_rstn; ++ u8 sdc_frac_en; ++ u8 sdc_rstn; ++ u8 sdm_deno; ++ u8 sdm_num_sign; ++ u8 sdm_num; ++ u8 sdc_n; ++ u8 sdc_n2; ++ u8 sdc_num; ++ u8 sdc_deno; ++ u8 sdc_ndiv_rstn; ++ u8 ssc_en; ++ u8 ssc_fm_dev; ++ u8 ssc_fm_freq; ++ u8 ssc_clk_div_sel; ++ u8 cd_tx_ser_rate_sel; ++}; ++ ++struct ropll_config { ++ u32 bit_rate; ++ u8 pms_mdiv; ++ u8 pms_mdiv_afc; ++ u8 pms_pdiv; ++ u8 pms_refdiv; ++ u8 pms_sdiv; ++ u8 pms_iqdiv_rstn; ++ u8 ref_clk_sel; ++ u8 sdm_en; ++ u8 sdm_rstn; ++ u8 sdc_frac_en; ++ u8 sdc_rstn; ++ u8 sdm_clk_div; ++ u8 sdm_deno; ++ u8 sdm_num_sign; ++ u8 sdm_num; ++ u8 sdc_n; ++ u8 sdc_num; ++ u8 sdc_deno; ++ u8 sdc_ndiv_rstn; ++ u8 ssc_en; ++ u8 ssc_fm_dev; ++ u8 ssc_fm_freq; ++ u8 ssc_clk_div_sel; ++ u8 ana_cpp_ctrl; ++ u8 ana_lpf_c_sel; ++ u8 cd_tx_ser_rate_sel; ++}; ++ ++enum rk_hdptx_reset { ++ RST_PHY = 0, ++ RST_APB, ++ RST_INIT, ++ RST_CMN, ++ RST_LANE, ++ RST_ROPLL, ++ RST_LCPLL, ++ RST_MAX ++}; ++ ++struct rk_hdptx_phy { ++ struct device *dev; ++ struct regmap *regmap; ++ struct regmap *grf; ++ ++ struct phy *phy; ++ struct phy_config *phy_cfg; ++ struct clk_bulk_data *clks; ++ int nr_clks; ++ struct reset_control_bulk_data rsts[RST_MAX]; ++}; ++ ++static const struct ropll_config ropll_tmds_cfg[] = { ++ { 5940000, 124, 124, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 3712500, 155, 155, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 2970000, 124, 124, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 1620000, 135, 135, 1, 1, 3, 1, 1, 0, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 1856250, 155, 155, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 1540000, 193, 193, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 193, 1, 32, 2, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 1485000, 0x7b, 0x7b, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 0, 3, 5, 5, ++ 0x10, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 1462500, 122, 122, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 244, 1, 16, 2, 1, 1, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 1190000, 149, 149, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 149, 1, 16, 2, 1, 1, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 1065000, 89, 89, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 89, 1, 16, 1, 0, 1, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 1080000, 135, 135, 1, 1, 5, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, ++ 0x14, 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 855000, 214, 214, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 214, 1, 16, 2, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 835000, 105, 105, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 42, 1, 16, 1, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 928125, 155, 155, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 742500, 124, 124, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 650000, 162, 162, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 54, 0, 16, 4, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 337500, 0x70, 0x70, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 0x2, 0, 0x01, 5, ++ 1, 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 400000, 100, 100, 1, 1, 11, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, ++ 0x14, 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 270000, 0x5a, 0x5a, 1, 1, 0xf, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, ++ 0x14, 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++ { 251750, 84, 84, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 168, 1, 16, 4, 1, 1, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, }, ++}; ++ ++static const struct reg_sequence rk_hdtpx_common_cmn_init_seq[] = { ++ REG_SEQ0(CMN_REG(0009), 0x0c), ++ REG_SEQ0(CMN_REG(000a), 0x83), ++ REG_SEQ0(CMN_REG(000b), 0x06), ++ REG_SEQ0(CMN_REG(000c), 0x20), ++ REG_SEQ0(CMN_REG(000d), 0xb8), ++ REG_SEQ0(CMN_REG(000e), 0x0f), ++ REG_SEQ0(CMN_REG(000f), 0x0f), ++ REG_SEQ0(CMN_REG(0010), 0x04), ++ REG_SEQ0(CMN_REG(0011), 0x00), ++ REG_SEQ0(CMN_REG(0012), 0x26), ++ REG_SEQ0(CMN_REG(0013), 0x22), ++ REG_SEQ0(CMN_REG(0014), 0x24), ++ REG_SEQ0(CMN_REG(0015), 0x77), ++ REG_SEQ0(CMN_REG(0016), 0x08), ++ REG_SEQ0(CMN_REG(0017), 0x00), ++ REG_SEQ0(CMN_REG(0018), 0x04), ++ REG_SEQ0(CMN_REG(0019), 0x48), ++ REG_SEQ0(CMN_REG(001a), 0x01), ++ REG_SEQ0(CMN_REG(001b), 0x00), ++ REG_SEQ0(CMN_REG(001c), 0x01), ++ REG_SEQ0(CMN_REG(001d), 0x64), ++ REG_SEQ0(CMN_REG(001f), 0x00), ++ REG_SEQ0(CMN_REG(0026), 0x53), ++ REG_SEQ0(CMN_REG(0029), 0x01), ++ REG_SEQ0(CMN_REG(0030), 0x00), ++ REG_SEQ0(CMN_REG(0031), 0x20), ++ REG_SEQ0(CMN_REG(0032), 0x30), ++ REG_SEQ0(CMN_REG(0033), 0x0b), ++ REG_SEQ0(CMN_REG(0034), 0x23), ++ REG_SEQ0(CMN_REG(0035), 0x00), ++ REG_SEQ0(CMN_REG(0038), 0x00), ++ REG_SEQ0(CMN_REG(0039), 0x00), ++ REG_SEQ0(CMN_REG(003a), 0x00), ++ REG_SEQ0(CMN_REG(003b), 0x00), ++ REG_SEQ0(CMN_REG(003c), 0x80), ++ REG_SEQ0(CMN_REG(003e), 0x0c), ++ REG_SEQ0(CMN_REG(003f), 0x83), ++ REG_SEQ0(CMN_REG(0040), 0x06), ++ REG_SEQ0(CMN_REG(0041), 0x20), ++ REG_SEQ0(CMN_REG(0042), 0xb8), ++ REG_SEQ0(CMN_REG(0043), 0x00), ++ REG_SEQ0(CMN_REG(0044), 0x46), ++ REG_SEQ0(CMN_REG(0045), 0x24), ++ REG_SEQ0(CMN_REG(0046), 0xff), ++ REG_SEQ0(CMN_REG(0047), 0x00), ++ REG_SEQ0(CMN_REG(0048), 0x44), ++ REG_SEQ0(CMN_REG(0049), 0xfa), ++ REG_SEQ0(CMN_REG(004a), 0x08), ++ REG_SEQ0(CMN_REG(004b), 0x00), ++ REG_SEQ0(CMN_REG(004c), 0x01), ++ REG_SEQ0(CMN_REG(004d), 0x64), ++ REG_SEQ0(CMN_REG(004e), 0x14), ++ REG_SEQ0(CMN_REG(004f), 0x00), ++ REG_SEQ0(CMN_REG(0050), 0x00), ++ REG_SEQ0(CMN_REG(005d), 0x0c), ++ REG_SEQ0(CMN_REG(005f), 0x01), ++ REG_SEQ0(CMN_REG(006b), 0x04), ++ REG_SEQ0(CMN_REG(0073), 0x30), ++ REG_SEQ0(CMN_REG(0074), 0x00), ++ REG_SEQ0(CMN_REG(0075), 0x20), ++ REG_SEQ0(CMN_REG(0076), 0x30), ++ REG_SEQ0(CMN_REG(0077), 0x08), ++ REG_SEQ0(CMN_REG(0078), 0x0c), ++ REG_SEQ0(CMN_REG(0079), 0x00), ++ REG_SEQ0(CMN_REG(007b), 0x00), ++ REG_SEQ0(CMN_REG(007c), 0x00), ++ REG_SEQ0(CMN_REG(007d), 0x00), ++ REG_SEQ0(CMN_REG(007e), 0x00), ++ REG_SEQ0(CMN_REG(007f), 0x00), ++ REG_SEQ0(CMN_REG(0080), 0x00), ++ REG_SEQ0(CMN_REG(0081), 0x09), ++ REG_SEQ0(CMN_REG(0082), 0x04), ++ REG_SEQ0(CMN_REG(0083), 0x24), ++ REG_SEQ0(CMN_REG(0084), 0x20), ++ REG_SEQ0(CMN_REG(0085), 0x03), ++ REG_SEQ0(CMN_REG(0086), 0x01), ++ REG_SEQ0(CMN_REG(0087), 0x0c), ++ REG_SEQ0(CMN_REG(008a), 0x55), ++ REG_SEQ0(CMN_REG(008b), 0x25), ++ REG_SEQ0(CMN_REG(008c), 0x2c), ++ REG_SEQ0(CMN_REG(008d), 0x22), ++ REG_SEQ0(CMN_REG(008e), 0x14), ++ REG_SEQ0(CMN_REG(008f), 0x20), ++ REG_SEQ0(CMN_REG(0090), 0x00), ++ REG_SEQ0(CMN_REG(0091), 0x00), ++ REG_SEQ0(CMN_REG(0092), 0x00), ++ REG_SEQ0(CMN_REG(0093), 0x00), ++ REG_SEQ0(CMN_REG(009a), 0x11), ++ REG_SEQ0(CMN_REG(009b), 0x10), ++}; ++ ++static const struct reg_sequence rk_hdtpx_tmds_cmn_init_seq[] = { ++ REG_SEQ0(CMN_REG(0008), 0x00), ++ REG_SEQ0(CMN_REG(0011), 0x01), ++ REG_SEQ0(CMN_REG(0017), 0x20), ++ REG_SEQ0(CMN_REG(001e), 0x14), ++ REG_SEQ0(CMN_REG(0020), 0x00), ++ REG_SEQ0(CMN_REG(0021), 0x00), ++ REG_SEQ0(CMN_REG(0022), 0x11), ++ REG_SEQ0(CMN_REG(0023), 0x00), ++ REG_SEQ0(CMN_REG(0024), 0x00), ++ REG_SEQ0(CMN_REG(0025), 0x53), ++ REG_SEQ0(CMN_REG(0026), 0x00), ++ REG_SEQ0(CMN_REG(0027), 0x00), ++ REG_SEQ0(CMN_REG(0028), 0x01), ++ REG_SEQ0(CMN_REG(002a), 0x00), ++ REG_SEQ0(CMN_REG(002b), 0x00), ++ REG_SEQ0(CMN_REG(002c), 0x00), ++ REG_SEQ0(CMN_REG(002d), 0x00), ++ REG_SEQ0(CMN_REG(002e), 0x04), ++ REG_SEQ0(CMN_REG(002f), 0x00), ++ REG_SEQ0(CMN_REG(0030), 0x20), ++ REG_SEQ0(CMN_REG(0031), 0x30), ++ REG_SEQ0(CMN_REG(0032), 0x0b), ++ REG_SEQ0(CMN_REG(0033), 0x23), ++ REG_SEQ0(CMN_REG(0034), 0x00), ++ REG_SEQ0(CMN_REG(003d), 0x40), ++ REG_SEQ0(CMN_REG(0042), 0x78), ++ REG_SEQ0(CMN_REG(004e), 0x34), ++ REG_SEQ0(CMN_REG(005c), 0x25), ++ REG_SEQ0(CMN_REG(005e), 0x4f), ++ REG_SEQ0(CMN_REG(0074), 0x04), ++ REG_SEQ0(CMN_REG(0081), 0x01), ++ REG_SEQ0(CMN_REG(0087), 0x04), ++ REG_SEQ0(CMN_REG(0089), 0x00), ++ REG_SEQ0(CMN_REG(0095), 0x00), ++ REG_SEQ0(CMN_REG(0097), 0x02), ++ REG_SEQ0(CMN_REG(0099), 0x04), ++ REG_SEQ0(CMN_REG(009b), 0x00), ++}; ++ ++static const struct reg_sequence rk_hdtpx_common_sb_init_seq[] = { ++ REG_SEQ0(SB_REG(0114), 0x00), ++ REG_SEQ0(SB_REG(0115), 0x00), ++ REG_SEQ0(SB_REG(0116), 0x00), ++ REG_SEQ0(SB_REG(0117), 0x00), ++}; ++ ++static const struct reg_sequence rk_hdtpx_tmds_lntop_highbr_seq[] = { ++ REG_SEQ0(LNTOP_REG(0201), 0x00), ++ REG_SEQ0(LNTOP_REG(0202), 0x00), ++ REG_SEQ0(LNTOP_REG(0203), 0x0f), ++ REG_SEQ0(LNTOP_REG(0204), 0xff), ++ REG_SEQ0(LNTOP_REG(0205), 0xff), ++}; ++ ++static const struct reg_sequence rk_hdtpx_tmds_lntop_lowbr_seq[] = { ++ REG_SEQ0(LNTOP_REG(0201), 0x07), ++ REG_SEQ0(LNTOP_REG(0202), 0xc1), ++ REG_SEQ0(LNTOP_REG(0203), 0xf0), ++ REG_SEQ0(LNTOP_REG(0204), 0x7c), ++ REG_SEQ0(LNTOP_REG(0205), 0x1f), ++}; ++ ++static const struct reg_sequence rk_hdtpx_common_lane_init_seq[] = { ++ REG_SEQ0(LANE_REG(0303), 0x0c), ++ REG_SEQ0(LANE_REG(0307), 0x20), ++ REG_SEQ0(LANE_REG(030a), 0x17), ++ REG_SEQ0(LANE_REG(030b), 0x77), ++ REG_SEQ0(LANE_REG(030c), 0x77), ++ REG_SEQ0(LANE_REG(030d), 0x77), ++ REG_SEQ0(LANE_REG(030e), 0x38), ++ REG_SEQ0(LANE_REG(0310), 0x03), ++ REG_SEQ0(LANE_REG(0311), 0x0f), ++ REG_SEQ0(LANE_REG(0316), 0x02), ++ REG_SEQ0(LANE_REG(031b), 0x01), ++ REG_SEQ0(LANE_REG(031f), 0x15), ++ REG_SEQ0(LANE_REG(0320), 0xa0), ++ REG_SEQ0(LANE_REG(0403), 0x0c), ++ REG_SEQ0(LANE_REG(0407), 0x20), ++ REG_SEQ0(LANE_REG(040a), 0x17), ++ REG_SEQ0(LANE_REG(040b), 0x77), ++ REG_SEQ0(LANE_REG(040c), 0x77), ++ REG_SEQ0(LANE_REG(040d), 0x77), ++ REG_SEQ0(LANE_REG(040e), 0x38), ++ REG_SEQ0(LANE_REG(0410), 0x03), ++ REG_SEQ0(LANE_REG(0411), 0x0f), ++ REG_SEQ0(LANE_REG(0416), 0x02), ++ REG_SEQ0(LANE_REG(041b), 0x01), ++ REG_SEQ0(LANE_REG(041f), 0x15), ++ REG_SEQ0(LANE_REG(0420), 0xa0), ++ REG_SEQ0(LANE_REG(0503), 0x0c), ++ REG_SEQ0(LANE_REG(0507), 0x20), ++ REG_SEQ0(LANE_REG(050a), 0x17), ++ REG_SEQ0(LANE_REG(050b), 0x77), ++ REG_SEQ0(LANE_REG(050c), 0x77), ++ REG_SEQ0(LANE_REG(050d), 0x77), ++ REG_SEQ0(LANE_REG(050e), 0x38), ++ REG_SEQ0(LANE_REG(0510), 0x03), ++ REG_SEQ0(LANE_REG(0511), 0x0f), ++ REG_SEQ0(LANE_REG(0516), 0x02), ++ REG_SEQ0(LANE_REG(051b), 0x01), ++ REG_SEQ0(LANE_REG(051f), 0x15), ++ REG_SEQ0(LANE_REG(0520), 0xa0), ++ REG_SEQ0(LANE_REG(0603), 0x0c), ++ REG_SEQ0(LANE_REG(0607), 0x20), ++ REG_SEQ0(LANE_REG(060a), 0x17), ++ REG_SEQ0(LANE_REG(060b), 0x77), ++ REG_SEQ0(LANE_REG(060c), 0x77), ++ REG_SEQ0(LANE_REG(060d), 0x77), ++ REG_SEQ0(LANE_REG(060e), 0x38), ++ REG_SEQ0(LANE_REG(0610), 0x03), ++ REG_SEQ0(LANE_REG(0611), 0x0f), ++ REG_SEQ0(LANE_REG(0616), 0x02), ++ REG_SEQ0(LANE_REG(061b), 0x01), ++ REG_SEQ0(LANE_REG(061f), 0x15), ++ REG_SEQ0(LANE_REG(0620), 0xa0), ++}; ++ ++static const struct reg_sequence rk_hdtpx_tmds_lane_init_seq[] = { ++ REG_SEQ0(LANE_REG(0312), 0x00), ++ REG_SEQ0(LANE_REG(031e), 0x00), ++ REG_SEQ0(LANE_REG(0412), 0x00), ++ REG_SEQ0(LANE_REG(041e), 0x00), ++ REG_SEQ0(LANE_REG(0512), 0x00), ++ REG_SEQ0(LANE_REG(051e), 0x00), ++ REG_SEQ0(LANE_REG(0612), 0x00), ++ REG_SEQ0(LANE_REG(061e), 0x08), ++ REG_SEQ0(LANE_REG(0303), 0x2f), ++ REG_SEQ0(LANE_REG(0403), 0x2f), ++ REG_SEQ0(LANE_REG(0503), 0x2f), ++ REG_SEQ0(LANE_REG(0603), 0x2f), ++ REG_SEQ0(LANE_REG(0305), 0x03), ++ REG_SEQ0(LANE_REG(0405), 0x03), ++ REG_SEQ0(LANE_REG(0505), 0x03), ++ REG_SEQ0(LANE_REG(0605), 0x03), ++ REG_SEQ0(LANE_REG(0306), 0x1c), ++ REG_SEQ0(LANE_REG(0406), 0x1c), ++ REG_SEQ0(LANE_REG(0506), 0x1c), ++ REG_SEQ0(LANE_REG(0606), 0x1c), ++}; ++ ++static bool rk_hdptx_phy_is_rw_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case 0x0000 ... 0x029c: ++ case 0x0400 ... 0x04a4: ++ case 0x0800 ... 0x08a4: ++ case 0x0c00 ... 0x0cb4: ++ case 0x1000 ... 0x10b4: ++ case 0x1400 ... 0x14b4: ++ case 0x1800 ... 0x18b4: ++ return true; ++ } ++ ++ return false; ++} ++ ++static const struct regmap_config rk_hdptx_phy_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .writeable_reg = rk_hdptx_phy_is_rw_reg, ++ .readable_reg = rk_hdptx_phy_is_rw_reg, ++ .fast_io = true, ++ .max_register = 0x18b4, ++}; ++ ++#define rk_hdptx_multi_reg_write(hdptx, seq) \ ++ regmap_multi_reg_write((hdptx)->regmap, seq, ARRAY_SIZE(seq)) ++ ++static void rk_hdptx_pre_power_up(struct rk_hdptx_phy *hdptx) ++{ ++ u32 val; ++ ++ reset_control_assert(hdptx->rsts[RST_APB].rstc); ++ usleep_range(20, 25); ++ reset_control_deassert(hdptx->rsts[RST_APB].rstc); ++ ++ reset_control_assert(hdptx->rsts[RST_LANE].rstc); ++ reset_control_assert(hdptx->rsts[RST_CMN].rstc); ++ reset_control_assert(hdptx->rsts[RST_INIT].rstc); ++ ++ val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16; ++ regmap_write(hdptx->grf, GRF_HDPTX_CON0, val); ++} ++ ++static int rk_hdptx_post_enable_lane(struct rk_hdptx_phy *hdptx) ++{ ++ u32 val; ++ int ret; ++ ++ reset_control_deassert(hdptx->rsts[RST_LANE].rstc); ++ ++ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | ++ HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN; ++ regmap_write(hdptx->grf, GRF_HDPTX_CON0, val); ++ ++ ret = regmap_read_poll_timeout(hdptx->grf, GRF_HDPTX_STATUS, val, ++ (val & HDPTX_O_PHY_RDY) && ++ (val & HDPTX_O_PLL_LOCK_DONE), ++ 100, 5000); ++ if (ret) { ++ dev_err(hdptx->dev, "Failed to get PHY lane lock: %d\n", ret); ++ return ret; ++ } ++ ++ dev_dbg(hdptx->dev, "PHY lane locked\n"); ++ ++ return 0; ++} ++ ++static int rk_hdptx_post_enable_pll(struct rk_hdptx_phy *hdptx) ++{ ++ u32 val; ++ int ret; ++ ++ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | ++ HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN; ++ regmap_write(hdptx->grf, GRF_HDPTX_CON0, val); ++ ++ usleep_range(10, 15); ++ reset_control_deassert(hdptx->rsts[RST_INIT].rstc); ++ ++ usleep_range(10, 15); ++ val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN; ++ regmap_write(hdptx->grf, GRF_HDPTX_CON0, val); ++ ++ usleep_range(10, 15); ++ reset_control_deassert(hdptx->rsts[RST_CMN].rstc); ++ ++ ret = regmap_read_poll_timeout(hdptx->grf, GRF_HDPTX_STATUS, val, ++ val & HDPTX_O_PHY_CLK_RDY, 20, 400); ++ if (ret) { ++ dev_err(hdptx->dev, "Failed to get PHY clk ready: %d\n", ret); ++ return ret; ++ } ++ ++ dev_dbg(hdptx->dev, "PHY clk ready\n"); ++ ++ return 0; ++} ++ ++static void rk_hdptx_phy_disable(struct rk_hdptx_phy *hdptx) ++{ ++ u32 val; ++ ++ /* reset phy and apb, or phy locked flag may keep 1 */ ++ reset_control_assert(hdptx->rsts[RST_PHY].rstc); ++ usleep_range(20, 30); ++ reset_control_deassert(hdptx->rsts[RST_PHY].rstc); ++ ++ reset_control_assert(hdptx->rsts[RST_APB].rstc); ++ usleep_range(20, 30); ++ reset_control_deassert(hdptx->rsts[RST_APB].rstc); ++ ++ regmap_write(hdptx->regmap, LANE_REG(0300), 0x82); ++ regmap_write(hdptx->regmap, SB_REG(010f), 0xc1); ++ regmap_write(hdptx->regmap, SB_REG(0110), 0x1); ++ regmap_write(hdptx->regmap, LANE_REG(0301), 0x80); ++ regmap_write(hdptx->regmap, LANE_REG(0401), 0x80); ++ regmap_write(hdptx->regmap, LANE_REG(0501), 0x80); ++ regmap_write(hdptx->regmap, LANE_REG(0601), 0x80); ++ ++ reset_control_assert(hdptx->rsts[RST_LANE].rstc); ++ reset_control_assert(hdptx->rsts[RST_CMN].rstc); ++ reset_control_assert(hdptx->rsts[RST_INIT].rstc); ++ ++ val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16; ++ regmap_write(hdptx->grf, GRF_HDPTX_CON0, val); ++} ++ ++static bool rk_hdptx_phy_clk_pll_calc(unsigned int data_rate, ++ struct ropll_config *cfg) ++{ ++ const unsigned int fout = data_rate / 2, fref = 24000; ++ unsigned long k = 0, lc, k_sub, lc_sub; ++ unsigned int fvco, sdc; ++ u32 mdiv, sdiv, n = 8; ++ ++ if (fout > 0xfffffff) ++ return false; ++ ++ for (sdiv = 16; sdiv >= 1; sdiv--) { ++ if (sdiv % 2 && sdiv != 1) ++ continue; ++ ++ fvco = fout * sdiv; ++ ++ if (fvco < 2000000 || fvco > 4000000) ++ continue; ++ ++ mdiv = DIV_ROUND_UP(fvco, fref); ++ if (mdiv < 20 || mdiv > 255) ++ continue; ++ ++ if (fref * mdiv - fvco) { ++ for (sdc = 264000; sdc <= 750000; sdc += fref) ++ if (sdc * n > fref * mdiv) ++ break; ++ ++ if (sdc > 750000) ++ continue; ++ ++ rational_best_approximation(fref * mdiv - fvco, ++ sdc / 16, ++ GENMASK(6, 0), ++ GENMASK(7, 0), ++ &k, &lc); ++ ++ rational_best_approximation(sdc * n - fref * mdiv, ++ sdc, ++ GENMASK(6, 0), ++ GENMASK(7, 0), ++ &k_sub, &lc_sub); ++ } ++ ++ break; ++ } ++ ++ if (sdiv < 1) ++ return false; ++ ++ if (cfg) { ++ cfg->pms_mdiv = mdiv; ++ cfg->pms_mdiv_afc = mdiv; ++ cfg->pms_pdiv = 1; ++ cfg->pms_refdiv = 1; ++ cfg->pms_sdiv = sdiv - 1; ++ ++ cfg->sdm_en = k > 0 ? 1 : 0; ++ if (cfg->sdm_en) { ++ cfg->sdm_deno = lc; ++ cfg->sdm_num_sign = 1; ++ cfg->sdm_num = k; ++ cfg->sdc_n = n - 3; ++ cfg->sdc_num = k_sub; ++ cfg->sdc_deno = lc_sub; ++ } ++ } ++ ++ return true; ++} ++ ++static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx, ++ unsigned int rate) ++{ ++ const struct ropll_config *cfg = NULL; ++ struct ropll_config rc = {0}; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ropll_tmds_cfg); i++) ++ if (rate == ropll_tmds_cfg[i].bit_rate) { ++ cfg = &ropll_tmds_cfg[i]; ++ break; ++ } ++ ++ if (!cfg) { ++ if (rk_hdptx_phy_clk_pll_calc(rate, &rc)) { ++ cfg = &rc; ++ } else { ++ dev_err(hdptx->dev, "%s cannot find pll cfg\n", __func__); ++ return -EINVAL; ++ } ++ } ++ ++ dev_dbg(hdptx->dev, "mdiv=%u, sdiv=%u, sdm_en=%u, k_sign=%u, k=%u, lc=%u\n", ++ cfg->pms_mdiv, cfg->pms_sdiv + 1, cfg->sdm_en, ++ cfg->sdm_num_sign, cfg->sdm_num, cfg->sdm_deno); ++ ++ rk_hdptx_pre_power_up(hdptx); ++ ++ reset_control_assert(hdptx->rsts[RST_ROPLL].rstc); ++ usleep_range(20, 30); ++ reset_control_deassert(hdptx->rsts[RST_ROPLL].rstc); ++ ++ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_cmn_init_seq); ++ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_tmds_cmn_init_seq); ++ ++ regmap_write(hdptx->regmap, CMN_REG(0051), cfg->pms_mdiv); ++ regmap_write(hdptx->regmap, CMN_REG(0055), cfg->pms_mdiv_afc); ++ regmap_write(hdptx->regmap, CMN_REG(0059), ++ (cfg->pms_pdiv << 4) | cfg->pms_refdiv); ++ regmap_write(hdptx->regmap, CMN_REG(005a), cfg->pms_sdiv << 4); ++ ++ regmap_update_bits(hdptx->regmap, CMN_REG(005e), ROPLL_SDM_EN_MASK, ++ FIELD_PREP(ROPLL_SDM_EN_MASK, cfg->sdm_en)); ++ if (!cfg->sdm_en) ++ regmap_update_bits(hdptx->regmap, CMN_REG(005e), 0xf, 0); ++ ++ regmap_update_bits(hdptx->regmap, CMN_REG(0064), ROPLL_SDM_NUM_SIGN_RBR_MASK, ++ FIELD_PREP(ROPLL_SDM_NUM_SIGN_RBR_MASK, cfg->sdm_num_sign)); ++ ++ regmap_write(hdptx->regmap, CMN_REG(0060), cfg->sdm_deno); ++ regmap_write(hdptx->regmap, CMN_REG(0065), cfg->sdm_num); ++ ++ regmap_update_bits(hdptx->regmap, CMN_REG(0069), ROPLL_SDC_N_RBR_MASK, ++ FIELD_PREP(ROPLL_SDC_N_RBR_MASK, cfg->sdc_n)); ++ ++ regmap_write(hdptx->regmap, CMN_REG(006c), cfg->sdc_num); ++ regmap_write(hdptx->regmap, CMN_REG(0070), cfg->sdc_deno); ++ ++ regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_POSTDIV_SEL_MASK, ++ FIELD_PREP(PLL_PCG_POSTDIV_SEL_MASK, cfg->pms_sdiv)); ++ ++ regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_CLK_EN, ++ PLL_PCG_CLK_EN); ++ ++ return rk_hdptx_post_enable_pll(hdptx); ++} ++ ++static int rk_hdptx_ropll_tmds_mode_config(struct rk_hdptx_phy *hdptx, ++ unsigned int rate) ++{ ++ u32 val; ++ int ret; ++ ++ ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &val); ++ if (ret) ++ return ret; ++ ++ if (!(val & HDPTX_O_PLL_LOCK_DONE)) { ++ ret = rk_hdptx_ropll_tmds_cmn_config(hdptx, rate); ++ if (ret) ++ return ret; ++ } ++ ++ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_sb_init_seq); ++ ++ regmap_write(hdptx->regmap, LNTOP_REG(0200), 0x06); ++ ++ if (rate >= 3400000) { ++ /* For 1/40 bitrate clk */ ++ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_tmds_lntop_highbr_seq); ++ } else { ++ /* For 1/10 bitrate clk */ ++ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_tmds_lntop_lowbr_seq); ++ } ++ ++ regmap_write(hdptx->regmap, LNTOP_REG(0206), 0x07); ++ regmap_write(hdptx->regmap, LNTOP_REG(0207), 0x0f); ++ ++ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_lane_init_seq); ++ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_tmds_lane_init_seq); ++ ++ return rk_hdptx_post_enable_lane(hdptx); ++} ++ ++static int rk_hdptx_phy_power_on(struct phy *phy) ++{ ++ struct rk_hdptx_phy *hdptx = phy_get_drvdata(phy); ++ int ret, bus_width = phy_get_bus_width(hdptx->phy); ++ /* ++ * FIXME: Temporary workaround to pass pixel_clk_rate ++ * from the HDMI bridge driver until phy_configure_opts_hdmi ++ * becomes available in the PHY API. ++ */ ++ unsigned int rate = bus_width & 0xfffffff; ++ ++ dev_dbg(hdptx->dev, "%s bus_width=%x rate=%u\n", ++ __func__, bus_width, rate); ++ ++ ret = pm_runtime_resume_and_get(hdptx->dev); ++ if (ret) { ++ dev_err(hdptx->dev, "Failed to resume phy: %d\n", ret); ++ return ret; ++ } ++ ++ ret = rk_hdptx_ropll_tmds_mode_config(hdptx, rate); ++ if (ret) ++ pm_runtime_put(hdptx->dev); ++ ++ return ret; ++} ++ ++static int rk_hdptx_phy_power_off(struct phy *phy) ++{ ++ struct rk_hdptx_phy *hdptx = phy_get_drvdata(phy); ++ u32 val; ++ int ret; ++ ++ ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &val); ++ if (ret == 0 && (val & HDPTX_O_PLL_LOCK_DONE)) ++ rk_hdptx_phy_disable(hdptx); ++ ++ pm_runtime_put(hdptx->dev); ++ ++ return ret; ++} ++ ++static const struct phy_ops rk_hdptx_phy_ops = { ++ .power_on = rk_hdptx_phy_power_on, ++ .power_off = rk_hdptx_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int rk_hdptx_phy_runtime_suspend(struct device *dev) ++{ ++ struct rk_hdptx_phy *hdptx = dev_get_drvdata(dev); ++ ++ clk_bulk_disable_unprepare(hdptx->nr_clks, hdptx->clks); ++ ++ return 0; ++} ++ ++static int rk_hdptx_phy_runtime_resume(struct device *dev) ++{ ++ struct rk_hdptx_phy *hdptx = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_bulk_prepare_enable(hdptx->nr_clks, hdptx->clks); ++ if (ret) ++ dev_err(hdptx->dev, "Failed to enable clocks: %d\n", ret); ++ ++ return ret; ++} ++ ++static int rk_hdptx_phy_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct device *dev = &pdev->dev; ++ struct rk_hdptx_phy *hdptx; ++ void __iomem *regs; ++ int ret; ++ ++ hdptx = devm_kzalloc(dev, sizeof(*hdptx), GFP_KERNEL); ++ if (!hdptx) ++ return -ENOMEM; ++ ++ hdptx->dev = dev; ++ ++ regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(regs)) ++ return dev_err_probe(dev, PTR_ERR(regs), ++ "Failed to ioremap resource\n"); ++ ++ ret = devm_clk_bulk_get_all(dev, &hdptx->clks); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "Failed to get clocks\n"); ++ if (ret == 0) ++ return dev_err_probe(dev, -EINVAL, "Missing clocks\n"); ++ ++ hdptx->nr_clks = ret; ++ ++ hdptx->regmap = devm_regmap_init_mmio(dev, regs, ++ &rk_hdptx_phy_regmap_config); ++ if (IS_ERR(hdptx->regmap)) ++ return dev_err_probe(dev, PTR_ERR(hdptx->regmap), ++ "Failed to init regmap\n"); ++ ++ hdptx->rsts[RST_PHY].id = "phy"; ++ hdptx->rsts[RST_APB].id = "apb"; ++ hdptx->rsts[RST_INIT].id = "init"; ++ hdptx->rsts[RST_CMN].id = "cmn"; ++ hdptx->rsts[RST_LANE].id = "lane"; ++ hdptx->rsts[RST_ROPLL].id = "ropll"; ++ hdptx->rsts[RST_LCPLL].id = "lcpll"; ++ ++ ret = devm_reset_control_bulk_get_exclusive(dev, RST_MAX, hdptx->rsts); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to get resets\n"); ++ ++ hdptx->grf = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "rockchip,grf"); ++ if (IS_ERR(hdptx->grf)) ++ return dev_err_probe(dev, PTR_ERR(hdptx->grf), ++ "Could not get GRF syscon\n"); ++ ++ hdptx->phy = devm_phy_create(dev, NULL, &rk_hdptx_phy_ops); ++ if (IS_ERR(hdptx->phy)) ++ return dev_err_probe(dev, PTR_ERR(hdptx->phy), ++ "Failed to create HDMI PHY\n"); ++ ++ platform_set_drvdata(pdev, hdptx); ++ phy_set_drvdata(hdptx->phy, hdptx); ++ phy_set_bus_width(hdptx->phy, 8); ++ ++ ret = devm_pm_runtime_enable(dev); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) ++ return dev_err_probe(dev, PTR_ERR(phy_provider), ++ "Failed to register PHY provider\n"); ++ ++ reset_control_deassert(hdptx->rsts[RST_APB].rstc); ++ reset_control_deassert(hdptx->rsts[RST_CMN].rstc); ++ reset_control_deassert(hdptx->rsts[RST_INIT].rstc); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops rk_hdptx_phy_pm_ops = { ++ RUNTIME_PM_OPS(rk_hdptx_phy_runtime_suspend, ++ rk_hdptx_phy_runtime_resume, NULL) ++}; ++ ++static const struct of_device_id rk_hdptx_phy_of_match[] = { ++ { .compatible = "rockchip,rk3588-hdptx-phy", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, rk_hdptx_phy_of_match); ++ ++static struct platform_driver rk_hdptx_phy_driver = { ++ .probe = rk_hdptx_phy_probe, ++ .driver = { ++ .name = "rockchip-hdptx-phy", ++ .pm = &rk_hdptx_phy_pm_ops, ++ .of_match_table = rk_hdptx_phy_of_match, ++ }, ++}; ++module_platform_driver(rk_hdptx_phy_driver); ++ ++MODULE_AUTHOR("Algea Cao "); ++MODULE_AUTHOR("Cristian Ciocaltea "); ++MODULE_DESCRIPTION("Samsung HDMI/eDP Transmitter Combo PHY Driver"); ++MODULE_LICENSE("GPL"); +-- +Armbian + diff --git a/patch/kernel/rockchip-rk3588-edge/0036-arm64-dts-rockchip-Add-HDMI0-PHY-to-rk3588.patch b/patch/kernel/rockchip-rk3588-edge/0036-arm64-dts-rockchip-Add-HDMI0-PHY-to-rk3588.patch new file mode 100644 index 0000000000..63267bebc7 --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0036-arm64-dts-rockchip-Add-HDMI0-PHY-to-rk3588.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Mon, 19 Feb 2024 22:46:25 +0200 +Subject: arm64: dts: rockchip: Add HDMI0 PHY to rk3588 + +Add DT nodes for HDMI0 PHY and related syscon found on RK3588 SoC. + +Signed-off-by: Cristian Ciocaltea +--- + arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 21 ++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +index 83f4d297e973..2dfcfe272eb2 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +@@ -1101,6 +1101,11 @@ u2phy3_host: host-port { + }; + }; + ++ hdptxphy0_grf: syscon@fd5e0000 { ++ compatible = "rockchip,rk3588-hdptxphy-grf", "syscon"; ++ reg = <0x0 0xfd5e0000 0x0 0x100>; ++ }; ++ + ioc: syscon@fd5f0000 { + compatible = "rockchip,rk3588-ioc", "syscon"; + reg = <0x0 0xfd5f0000 0x0 0x10000>; +@@ -2939,6 +2944,22 @@ usbdp_phy0: phy@fed80000 { + status = "disabled"; + }; + ++ hdptxphy_hdmi0: phy@fed60000 { ++ compatible = "rockchip,rk3588-hdptx-phy"; ++ reg = <0x0 0xfed60000 0x0 0x2000>; ++ clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>, <&cru PCLK_HDPTX0>; ++ clock-names = "ref", "apb"; ++ #phy-cells = <0>; ++ resets = <&cru SRST_HDPTX0>, <&cru SRST_P_HDPTX0>, ++ <&cru SRST_HDPTX0_INIT>, <&cru SRST_HDPTX0_CMN>, ++ <&cru SRST_HDPTX0_LANE>, <&cru SRST_HDPTX0_ROPLL>, ++ <&cru SRST_HDPTX0_LCPLL>; ++ reset-names = "phy", "apb", "init", "cmn", "lane", "ropll", ++ "lcpll"; ++ rockchip,grf = <&hdptxphy0_grf>; ++ status = "disabled"; ++ }; ++ + combphy0_ps: phy@fee00000 { + compatible = "rockchip,rk3588-naneng-combphy"; + reg = <0x0 0xfee00000 0x0 0x100>; +-- +Armbian + diff --git a/patch/kernel/rockchip-rk3588-edge/0037-drm-bridge-synopsys-Add-initial-support-for-DW-HDMI-.patch b/patch/kernel/rockchip-rk3588-edge/0037-drm-bridge-synopsys-Add-initial-support-for-DW-HDMI-.patch new file mode 100644 index 0000000000..052ed452f7 --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0037-drm-bridge-synopsys-Add-initial-support-for-DW-HDMI-.patch @@ -0,0 +1,6617 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Wed, 1 Nov 2023 18:50:38 +0200 +Subject: drm/bridge: synopsys: Add initial support for DW HDMI QP TX + Controller + +Co-developed-by: Algea Cao +Signed-off-by: Algea Cao +Signed-off-by: Cristian Ciocaltea +--- + drivers/gpu/drm/bridge/synopsys/Makefile | 2 +- + drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 2401 ++++++++ + drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h | 831 +++ + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 89 + + drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 4 + + drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2741 +++++++++- + include/drm/bridge/dw_hdmi.h | 101 + + 7 files changed, 6103 insertions(+), 66 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile +index ce715562e9e5..8354e4879f70 100644 +--- a/drivers/gpu/drm/bridge/synopsys/Makefile ++++ b/drivers/gpu/drm/bridge/synopsys/Makefile +@@ -1,5 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0-only +-obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o ++obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o dw-hdmi-qp.o + obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o + obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o + obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +new file mode 100644 +index 000000000000..8817ef9a9de9 +--- /dev/null ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +@@ -0,0 +1,2401 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) Rockchip Electronics Co.Ltd ++ * Author: ++ * Algea Cao ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "dw-hdmi-qp.h" ++ ++#define DDC_CI_ADDR 0x37 ++#define DDC_SEGMENT_ADDR 0x30 ++ ++#define HDMI_EDID_LEN 512 ++ ++/* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */ ++#define SCDC_MIN_SOURCE_VERSION 0x1 ++ ++#define HDMI14_MAX_TMDSCLK 340000000 ++#define HDMI20_MAX_TMDSCLK_KHZ 600000 ++ ++static const unsigned int dw_hdmi_cable[] = { ++ EXTCON_DISP_HDMI, ++ EXTCON_NONE, ++}; ++ ++static const struct drm_display_mode dw_hdmi_default_modes[] = { ++ /* 16 - 1920x1080@60Hz 16:9 */ ++ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, ++ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 2 - 720x480@60Hz 4:3 */ ++ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, ++ 798, 858, 0, 480, 489, 495, 525, 0, ++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, ++ /* 4 - 1280x720@60Hz 16:9 */ ++ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, ++ 1430, 1650, 0, 720, 725, 730, 750, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 31 - 1920x1080@50Hz 16:9 */ ++ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, ++ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 19 - 1280x720@50Hz 16:9 */ ++ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, ++ 1760, 1980, 0, 720, 725, 730, 750, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 17 - 720x576@50Hz 4:3 */ ++ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, ++ 796, 864, 0, 576, 581, 586, 625, 0, ++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, ++ /* 2 - 720x480@60Hz 4:3 */ ++ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, ++ 798, 858, 0, 480, 489, 495, 525, 0, ++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, ++}; ++ ++enum frl_mask { ++ FRL_3GBPS_3LANE = 1, ++ FRL_6GBPS_3LANE, ++ FRL_6GBPS_4LANE, ++ FRL_8GBPS_4LANE, ++ FRL_10GBPS_4LANE, ++ FRL_12GBPS_4LANE, ++}; ++ ++struct hdmi_vmode_qp { ++ bool mdataenablepolarity; ++ ++ unsigned int previous_pixelclock; ++ unsigned long mpixelclock; ++ unsigned int mpixelrepetitioninput; ++ unsigned int mpixelrepetitionoutput; ++ unsigned long previous_tmdsclock; ++ unsigned int mtmdsclock; ++}; ++ ++struct hdmi_qp_data_info { ++ unsigned int enc_in_bus_format; ++ unsigned int enc_out_bus_format; ++ unsigned int enc_in_encoding; ++ unsigned int enc_out_encoding; ++ unsigned int quant_range; ++ unsigned int pix_repet_factor; ++ struct hdmi_vmode_qp video_mode; ++ bool update; ++}; ++ ++struct dw_hdmi_qp_i2c { ++ struct i2c_adapter adap; ++ ++ struct mutex lock; /* used to serialize data transfers */ ++ struct completion cmp; ++ u32 stat; ++ ++ u8 slave_reg; ++ bool is_regaddr; ++ bool is_segment; ++ ++ unsigned int scl_high_ns; ++ unsigned int scl_low_ns; ++}; ++ ++struct dw_hdmi_qp { ++ struct drm_connector connector; ++ struct drm_bridge bridge; ++ struct platform_device *hdcp_dev; ++ struct platform_device *audio; ++ struct platform_device *cec; ++ struct device *dev; ++ struct dw_hdmi_qp_i2c *i2c; ++ ++ struct hdmi_qp_data_info hdmi_data; ++ const struct dw_hdmi_plat_data *plat_data; ++ ++ int vic; ++ int main_irq; ++ int avp_irq; ++ int earc_irq; ++ ++ u8 edid[HDMI_EDID_LEN]; ++ ++ struct { ++ const struct dw_hdmi_qp_phy_ops *ops; ++ const char *name; ++ void *data; ++ bool enabled; ++ } phy; ++ ++ struct drm_display_mode previous_mode; ++ ++ struct i2c_adapter *ddc; ++ void __iomem *regs; ++ bool sink_is_hdmi; ++ ++ struct mutex mutex; /* for state below and previous_mode */ ++ //[CC:] curr_conn should be removed ++ struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */ ++ enum drm_connector_force force; /* mutex-protected force state */ ++ bool disabled; /* DRM has disabled our bridge */ ++ bool bridge_is_on; /* indicates the bridge is on */ ++ bool rxsense; /* rxsense state */ ++ u8 phy_mask; /* desired phy int mask settings */ ++ u8 mc_clkdis; /* clock disable register */ ++ ++ u32 scdc_intr; ++ u32 flt_intr; ++ //[CC:] remove earc ++ u32 earc_intr; ++ ++ struct dentry *debugfs_dir; ++ bool scramble_low_rates; ++ ++ struct extcon_dev *extcon; ++ ++ struct regmap *regm; ++ ++ bool initialized; /* hdmi is enabled before bind */ ++ struct completion flt_cmp; ++ struct completion earc_cmp; ++ ++ hdmi_codec_plugged_cb plugged_cb; ++ struct device *codec_dev; ++ enum drm_connector_status last_connector_result; ++}; ++ ++static inline void hdmi_writel(struct dw_hdmi_qp *hdmi, u32 val, int offset) ++{ ++ regmap_write(hdmi->regm, offset, val); ++} ++ ++static inline u32 hdmi_readl(struct dw_hdmi_qp *hdmi, int offset) ++{ ++ unsigned int val = 0; ++ ++ regmap_read(hdmi->regm, offset, &val); ++ ++ return val; ++} ++ ++static void handle_plugged_change(struct dw_hdmi_qp *hdmi, bool plugged) ++{ ++ if (hdmi->plugged_cb && hdmi->codec_dev) ++ hdmi->plugged_cb(hdmi->codec_dev, plugged); ++} ++ ++static void hdmi_modb(struct dw_hdmi_qp *hdmi, u32 data, u32 mask, u32 reg) ++{ ++ regmap_update_bits(hdmi->regm, reg, mask, data); ++} ++ ++static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static int hdmi_bus_fmt_color_depth(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ return 8; ++ ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ return 10; ++ ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ return 12; ++ ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return 16; ++ ++ default: ++ return 0; ++ } ++} ++ ++static void dw_hdmi_i2c_init(struct dw_hdmi_qp *hdmi) ++{ ++ /* Software reset */ ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ ++ hdmi_writel(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0); ++ ++ hdmi_modb(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0); ++ ++ /* Clear DONE and ERROR interrupts */ ++ hdmi_writel(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR, ++ MAINUNIT_1_INT_CLEAR); ++} ++ ++static int dw_hdmi_i2c_read(struct dw_hdmi_qp *hdmi, ++ unsigned char *buf, unsigned int length) ++{ ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ int stat; ++ ++ if (!i2c->is_regaddr) { ++ dev_dbg(hdmi->dev, "set read register address to 0\n"); ++ i2c->slave_reg = 0x00; ++ i2c->is_regaddr = true; ++ } ++ ++ while (length--) { ++ reinit_completion(&i2c->cmp); ++ ++ hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, ++ I2CM_INTERFACE_CONTROL0); ++ ++ hdmi_modb(hdmi, I2CM_FM_READ, I2CM_WR_MASK, ++ I2CM_INTERFACE_CONTROL0); ++ ++ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); ++ if (!stat) { ++ dev_err(hdmi->dev, "i2c read time out!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EAGAIN; ++ } ++ ++ /* Check for error condition on the bus */ ++ if (i2c->stat & I2CM_NACK_RCVD_IRQ) { ++ dev_err(hdmi->dev, "i2c read err!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EIO; ++ } ++ ++ *buf++ = hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff; ++ hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); ++ } ++ i2c->is_segment = false; ++ ++ return 0; ++} ++ ++static int dw_hdmi_i2c_write(struct dw_hdmi_qp *hdmi, ++ unsigned char *buf, unsigned int length) ++{ ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ int stat; ++ ++ if (!i2c->is_regaddr) { ++ /* Use the first write byte as register address */ ++ i2c->slave_reg = buf[0]; ++ length--; ++ buf++; ++ i2c->is_regaddr = true; ++ } ++ ++ while (length--) { ++ reinit_completion(&i2c->cmp); ++ ++ hdmi_writel(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3); ++ hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, ++ I2CM_INTERFACE_CONTROL0); ++ hdmi_modb(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK, ++ I2CM_INTERFACE_CONTROL0); ++ ++ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); ++ if (!stat) { ++ dev_err(hdmi->dev, "i2c write time out!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EAGAIN; ++ } ++ ++ /* Check for error condition on the bus */ ++ if (i2c->stat & I2CM_NACK_RCVD_IRQ) { ++ dev_err(hdmi->dev, "i2c write nack!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EIO; ++ } ++ hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); ++ } ++ ++ return 0; ++} ++ ++static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, ++ struct i2c_msg *msgs, int num) ++{ ++ struct dw_hdmi_qp *hdmi = i2c_get_adapdata(adap); ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ u8 addr = msgs[0].addr; ++ int i, ret = 0; ++ ++ if (addr == DDC_CI_ADDR) ++ /* ++ * The internal I2C controller does not support the multi-byte ++ * read and write operations needed for DDC/CI. ++ * TOFIX: Blacklist the DDC/CI address until we filter out ++ * unsupported I2C operations. ++ */ ++ return -EOPNOTSUPP; ++ ++ for (i = 0; i < num; i++) { ++ if (msgs[i].len == 0) { ++ dev_err(hdmi->dev, ++ "unsupported transfer %d/%d, no data\n", ++ i + 1, num); ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ mutex_lock(&i2c->lock); ++ ++ /* Unmute DONE and ERROR interrupts */ ++ hdmi_modb(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, ++ I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, ++ MAINUNIT_1_INT_MASK_N); ++ ++ /* Set slave device address taken from the first I2C message */ ++ if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1) ++ addr = DDC_ADDR; ++ ++ hdmi_modb(hdmi, addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0); ++ ++ /* Set slave device register address on transfer */ ++ i2c->is_regaddr = false; ++ ++ /* Set segment pointer for I2C extended read mode operation */ ++ i2c->is_segment = false; ++ ++ for (i = 0; i < num; i++) { ++ if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { ++ i2c->is_segment = true; ++ hdmi_modb(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR, ++ I2CM_INTERFACE_CONTROL1); ++ hdmi_modb(hdmi, *msgs[i].buf, I2CM_SEG_PTR, ++ I2CM_INTERFACE_CONTROL1); ++ } else { ++ if (msgs[i].flags & I2C_M_RD) ++ ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, ++ msgs[i].len); ++ else ++ ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, ++ msgs[i].len); ++ } ++ if (ret < 0) ++ break; ++ } ++ ++ if (!ret) ++ ret = num; ++ ++ /* Mute DONE and ERROR interrupts */ ++ hdmi_modb(hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N, ++ MAINUNIT_1_INT_MASK_N); ++ ++ mutex_unlock(&i2c->lock); ++ ++ return ret; ++} ++ ++static u32 dw_hdmi_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; ++} ++ ++static const struct i2c_algorithm dw_hdmi_algorithm = { ++ .master_xfer = dw_hdmi_i2c_xfer, ++ .functionality = dw_hdmi_i2c_func, ++}; ++ ++static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi_qp *hdmi) ++{ ++ struct i2c_adapter *adap; ++ struct dw_hdmi_qp_i2c *i2c; ++ int ret; ++ ++ i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL); ++ if (!i2c) ++ return ERR_PTR(-ENOMEM); ++ ++ mutex_init(&i2c->lock); ++ init_completion(&i2c->cmp); ++ ++ adap = &i2c->adap; ++ adap->owner = THIS_MODULE; ++ adap->dev.parent = hdmi->dev; ++ adap->algo = &dw_hdmi_algorithm; ++ strscpy(adap->name, "ddc", sizeof(adap->name)); ++ i2c_set_adapdata(adap, hdmi); ++ ++ ret = i2c_add_adapter(adap); ++ if (ret) { ++ dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name); ++ devm_kfree(hdmi->dev, i2c); ++ return ERR_PTR(ret); ++ } ++ ++ hdmi->i2c = i2c; ++ ++ dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name); ++ ++ return adap; ++} ++ ++#define HDMI_PHY_EARC_MASK BIT(29) ++ ++/* ----------------------------------------------------------------------------- ++ * HDMI TX Setup ++ */ ++ ++static void hdmi_infoframe_set_checksum(u8 *ptr, int size) ++{ ++ u8 csum = 0; ++ int i; ++ ++ ptr[3] = 0; ++ /* compute checksum */ ++ for (i = 0; i < size; i++) ++ csum += ptr[i]; ++ ++ ptr[3] = 256 - csum; ++} ++ ++static void hdmi_config_AVI(struct dw_hdmi_qp *hdmi, ++ const struct drm_connector *connector, ++ const struct drm_display_mode *mode) ++{ ++ struct hdmi_avi_infoframe frame; ++ u32 val, i, j; ++ u8 buff[17]; ++ enum hdmi_quantization_range rgb_quant_range = ++ hdmi->hdmi_data.quant_range; ++ ++ /* Initialise info frame from DRM mode */ ++ drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode); ++ ++ /* ++ * Ignore monitor selectable quantization, use quantization set ++ * by the user ++ */ ++ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, rgb_quant_range); ++ if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) ++ frame.colorspace = HDMI_COLORSPACE_YUV444; ++ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) ++ frame.colorspace = HDMI_COLORSPACE_YUV422; ++ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ frame.colorspace = HDMI_COLORSPACE_YUV420; ++ else ++ frame.colorspace = HDMI_COLORSPACE_RGB; ++ ++ /* Set up colorimetry */ ++ if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { ++ switch (hdmi->hdmi_data.enc_out_encoding) { ++ case V4L2_YCBCR_ENC_601: ++ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601) ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ else ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_601; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; ++ break; ++ case V4L2_YCBCR_ENC_709: ++ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709) ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ else ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_709; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_709; ++ break; ++ case V4L2_YCBCR_ENC_BT2020: ++ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_BT2020) ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ else ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_709; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_BT2020; ++ break; ++ default: /* Carries no data */ ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_601; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; ++ break; ++ } ++ } else { ++ if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_BT2020; ++ } else { ++ frame.colorimetry = HDMI_COLORIMETRY_NONE; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; ++ } ++ } ++ ++ frame.scan_mode = HDMI_SCAN_MODE_NONE; ++ frame.video_code = hdmi->vic; ++ ++ hdmi_avi_infoframe_pack_only(&frame, buff, 17); ++ ++ /* mode which vic >= 128 must use avi version 3 */ ++ if (hdmi->vic >= 128) { ++ frame.version = 3; ++ buff[1] = frame.version; ++ buff[4] &= 0x1f; ++ buff[4] |= ((frame.colorspace & 0x7) << 5); ++ buff[7] = frame.video_code; ++ hdmi_infoframe_set_checksum(buff, 17); ++ } ++ ++ /* ++ * The Designware IP uses a different byte format from standard ++ * AVI info frames, though generally the bits are in the correct ++ * bytes. ++ */ ++ ++ val = (frame.version << 8) | (frame.length << 16); ++ hdmi_writel(hdmi, val, PKT_AVI_CONTENTS0); ++ ++ for (i = 0; i < 4; i++) { ++ for (j = 0; j < 4; j++) { ++ if (i * 4 + j >= 14) ++ break; ++ if (!j) ++ val = buff[i * 4 + j + 3]; ++ val |= buff[i * 4 + j + 3] << (8 * j); ++ } ++ ++ hdmi_writel(hdmi, val, PKT_AVI_CONTENTS1 + i * 4); ++ } ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1); ++ ++ hdmi_modb(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, ++ PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, ++ PKTSCHED_PKT_EN); ++} ++ ++static void hdmi_config_CVTEM(struct dw_hdmi_qp *hdmi) ++{ ++ u8 ds_type = 0; ++ u8 sync = 1; ++ u8 vfr = 1; ++ u8 afr = 0; ++ u8 new = 1; ++ u8 end = 0; ++ u8 data_set_length = 136; ++ u8 hb1[6] = { 0x80, 0, 0, 0, 0, 0x40 }; ++ u8 *pps_body; ++ u32 val, i, reg; ++ struct drm_display_mode *mode = &hdmi->previous_mode; ++ int hsync, hfront, hback; ++ struct dw_hdmi_link_config *link_cfg; ++ void *data = hdmi->plat_data->phy_data; ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_PKT_EN); ++ ++ if (hdmi->plat_data->get_link_cfg) { ++ link_cfg = hdmi->plat_data->get_link_cfg(data); ++ } else { ++ dev_err(hdmi->dev, "can't get frl link cfg\n"); ++ return; ++ } ++ ++ if (!link_cfg->dsc_mode) { ++ dev_info(hdmi->dev, "don't use dsc mode\n"); ++ return; ++ } ++ ++ pps_body = link_cfg->pps_payload; ++ ++ hsync = mode->hsync_end - mode->hsync_start; ++ hback = mode->htotal - mode->hsync_end; ++ hfront = mode->hsync_start - mode->hdisplay; ++ ++ for (i = 0; i < 6; i++) { ++ val = i << 16 | hb1[i] << 8; ++ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS0 + i * 0x20); ++ } ++ ++ val = new << 7 | end << 6 | ds_type << 4 | afr << 3 | ++ vfr << 2 | sync << 1; ++ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS1); ++ ++ val = data_set_length << 16 | pps_body[0] << 24; ++ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS2); ++ ++ reg = PKT0_EMP_CVTEM_CONTENTS3; ++ for (i = 1; i < 125; i++) { ++ if (reg == PKT1_EMP_CVTEM_CONTENTS0 || ++ reg == PKT2_EMP_CVTEM_CONTENTS0 || ++ reg == PKT3_EMP_CVTEM_CONTENTS0 || ++ reg == PKT4_EMP_CVTEM_CONTENTS0 || ++ reg == PKT5_EMP_CVTEM_CONTENTS0) { ++ reg += 4; ++ i--; ++ continue; ++ } ++ if (i % 4 == 1) ++ val = pps_body[i]; ++ if (i % 4 == 2) ++ val |= pps_body[i] << 8; ++ if (i % 4 == 3) ++ val |= pps_body[i] << 16; ++ if (!(i % 4)) { ++ val |= pps_body[i] << 24; ++ hdmi_writel(hdmi, val, reg); ++ reg += 4; ++ } ++ } ++ ++ val = (hfront & 0xff) << 24 | pps_body[127] << 16 | ++ pps_body[126] << 8 | pps_body[125]; ++ hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS6); ++ ++ val = (hback & 0xff) << 24 | ((hsync >> 8) & 0xff) << 16 | ++ (hsync & 0xff) << 8 | ((hfront >> 8) & 0xff); ++ hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS7); ++ ++ val = link_cfg->hcactive << 8 | ((hback >> 8) & 0xff); ++ hdmi_writel(hdmi, val, PKT5_EMP_CVTEM_CONTENTS1); ++ ++ for (i = PKT5_EMP_CVTEM_CONTENTS2; i <= PKT5_EMP_CVTEM_CONTENTS7; i += 4) ++ hdmi_writel(hdmi, 0, i); ++ ++ hdmi_modb(hdmi, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_EMP_CVTEM_TX_EN, ++ PKTSCHED_PKT_EN); ++} ++ ++static void hdmi_config_drm_infoframe(struct dw_hdmi_qp *hdmi, ++ const struct drm_connector *connector) ++{ ++ const struct drm_connector_state *conn_state = connector->state; ++ struct hdr_output_metadata *hdr_metadata; ++ struct hdmi_drm_infoframe frame; ++ u8 buffer[30]; ++ ssize_t err; ++ int i; ++ u32 val; ++ ++ if (!hdmi->plat_data->use_drm_infoframe) ++ return; ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); ++ ++ if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) { ++ DRM_DEBUG("No need to set HDR metadata in infoframe\n"); ++ return; ++ } ++ ++ if (!conn_state->hdr_output_metadata) { ++ DRM_DEBUG("source metadata not set yet\n"); ++ return; ++ } ++ ++ hdr_metadata = (struct hdr_output_metadata *) ++ conn_state->hdr_output_metadata->data; ++ ++ if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf & ++ BIT(hdr_metadata->hdmi_metadata_type1.eotf))) { ++ DRM_ERROR("Not support EOTF %d\n", ++ hdr_metadata->hdmi_metadata_type1.eotf); ++ return; ++ } ++ ++ err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); ++ if (err < 0) ++ return; ++ ++ err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer)); ++ if (err < 0) { ++ dev_err(hdmi->dev, "Failed to pack drm infoframe: %zd\n", err); ++ return; ++ } ++ ++ val = (frame.version << 8) | (frame.length << 16); ++ hdmi_writel(hdmi, val, PKT_DRMI_CONTENTS0); ++ ++ for (i = 0; i <= frame.length; i++) { ++ if (i % 4 == 0) ++ val = buffer[3 + i]; ++ val |= buffer[3 + i] << ((i % 4) * 8); ++ ++ if (i % 4 == 3 || (i == (frame.length))) ++ hdmi_writel(hdmi, val, PKT_DRMI_CONTENTS1 + ((i / 4) * 4)); ++ } ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1); ++ ++ hdmi_modb(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); ++ ++ DRM_DEBUG("%s eotf %d end\n", __func__, ++ hdr_metadata->hdmi_metadata_type1.eotf); ++} ++ ++/* Filter out invalid setups to avoid configuring SCDC and scrambling */ ++static bool dw_hdmi_support_scdc(struct dw_hdmi_qp *hdmi, ++ const struct drm_display_info *display) ++{ ++ /* Disable if no DDC bus */ ++ if (!hdmi->ddc) ++ return false; ++ ++ /* Disable if SCDC is not supported, or if an HF-VSDB block is absent */ ++ if (!display->hdmi.scdc.supported || ++ !display->hdmi.scdc.scrambling.supported) ++ return false; ++ ++ /* ++ * Disable if display only support low TMDS rates and scrambling ++ * for low rates is not supported either ++ */ ++ if (!display->hdmi.scdc.scrambling.low_rates && ++ display->max_tmds_clock <= 340000) ++ return false; ++ ++ return true; ++} ++ ++static int hdmi_set_frl_mask(int frl_rate) ++{ ++ switch (frl_rate) { ++ case 48: ++ return FRL_12GBPS_4LANE; ++ case 40: ++ return FRL_10GBPS_4LANE; ++ case 32: ++ return FRL_8GBPS_4LANE; ++ case 24: ++ return FRL_6GBPS_4LANE; ++ case 18: ++ return FRL_6GBPS_3LANE; ++ case 9: ++ return FRL_3GBPS_3LANE; ++ } ++ ++ return 0; ++} ++ ++static int hdmi_start_flt(struct dw_hdmi_qp *hdmi, u8 rate) ++{ ++ u8 val; ++ u8 ffe_lv = 0; ++ int i = 0, stat; ++ ++ /* FLT_READY & FFE_LEVELS read */ ++ for (i = 0; i < 20; i++) { ++ drm_scdc_readb(hdmi->ddc, SCDC_STATUS_FLAGS_0, &val); ++ if (val & BIT(6)) ++ break; ++ msleep(20); ++ } ++ ++ if (i == 20) { ++ dev_err(hdmi->dev, "sink flt isn't ready\n"); ++ return -EINVAL; ++ } ++ ++ hdmi_modb(hdmi, SCDC_UPD_FLAGS_RD_IRQ, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ hdmi_modb(hdmi, SCDC_UPD_FLAGS_POLL_EN | SCDC_UPD_FLAGS_AUTO_CLR, ++ SCDC_UPD_FLAGS_POLL_EN | SCDC_UPD_FLAGS_AUTO_CLR, ++ SCDC_CONFIG0); ++ ++ /* max ffe level 3 */ ++ val = 3 << 4 | hdmi_set_frl_mask(rate); ++ drm_scdc_writeb(hdmi->ddc, 0x31, val); ++ ++ /* select FRL_RATE & FFE_LEVELS */ ++ hdmi_writel(hdmi, ffe_lv, FLT_CONFIG0); ++ ++ /* Start LTS_3 state in source DUT */ ++ reinit_completion(&hdmi->flt_cmp); ++ hdmi_modb(hdmi, FLT_EXIT_TO_LTSP_IRQ, FLT_EXIT_TO_LTSP_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ hdmi_writel(hdmi, 1, FLT_CONTROL0); ++ ++ /* wait for completed link training at source side */ ++ stat = wait_for_completion_timeout(&hdmi->flt_cmp, HZ * 2); ++ if (!stat) { ++ dev_err(hdmi->dev, "wait lts3 finish time out\n"); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | ++ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ return -EAGAIN; ++ } ++ ++ if (!(hdmi->flt_intr & FLT_EXIT_TO_LTSP_IRQ)) { ++ dev_err(hdmi->dev, "not to ltsp\n"); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | ++ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++#define HDMI_MODE_FRL_MASK BIT(30) ++ ++static void hdmi_set_op_mode(struct dw_hdmi_qp *hdmi, ++ struct dw_hdmi_link_config *link_cfg, ++ const struct drm_connector *connector) ++{ ++ int frl_rate; ++ int i; ++ ++ /* set sink frl mode disable and wait sink ready */ ++ hdmi_writel(hdmi, 0, FLT_CONFIG0); ++ if (dw_hdmi_support_scdc(hdmi, &connector->display_info)) ++ drm_scdc_writeb(hdmi->ddc, 0x31, 0); ++ /* ++ * some TVs must wait a while before switching frl mode resolution, ++ * or the signal may not be recognized. ++ */ ++ msleep(200); ++ ++ if (!link_cfg->frl_mode) { ++ dev_info(hdmi->dev, "dw hdmi qp use tmds mode\n"); ++ hdmi_modb(hdmi, 0, OPMODE_FRL, LINK_CONFIG0); ++ hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0); ++ return; ++ } ++ ++ if (link_cfg->frl_lanes == 4) ++ hdmi_modb(hdmi, OPMODE_FRL_4LANES, OPMODE_FRL_4LANES, ++ LINK_CONFIG0); ++ else ++ hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0); ++ ++ hdmi_modb(hdmi, 1, OPMODE_FRL, LINK_CONFIG0); ++ ++ frl_rate = link_cfg->frl_lanes * link_cfg->rate_per_lane; ++ hdmi_start_flt(hdmi, frl_rate); ++ ++ for (i = 0; i < 50; i++) { ++ hdmi_modb(hdmi, PKTSCHED_NULL_TX_EN, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN); ++ mdelay(1); ++ hdmi_modb(hdmi, 0, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN); ++ } ++} ++ ++static unsigned long ++hdmi_get_tmdsclock(struct dw_hdmi_qp *hdmi, unsigned long mpixelclock) ++{ ++ unsigned long tmdsclock = mpixelclock; ++ unsigned int depth = ++ hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); ++ ++ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { ++ switch (depth) { ++ case 16: ++ tmdsclock = mpixelclock * 2; ++ break; ++ case 12: ++ tmdsclock = mpixelclock * 3 / 2; ++ break; ++ case 10: ++ tmdsclock = mpixelclock * 5 / 4; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return tmdsclock; ++} ++ ++//[CC:] is connector param different from hdmi->connector? ++//[CC:] probably it possible to hook the whole implementation into dw-hdmi.c ++static int dw_hdmi_qp_setup(struct dw_hdmi_qp *hdmi, ++ struct drm_connector *connector, ++ struct drm_display_mode *mode) ++{ ++ int ret; ++ void *data = hdmi->plat_data->phy_data; ++ struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode; ++ struct dw_hdmi_link_config *link_cfg; ++ u8 bytes = 0; ++ ++ hdmi->vic = drm_match_cea_mode(mode); ++ if (!hdmi->vic) ++ dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n"); ++ else ++ dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic); ++ ++ if (hdmi->plat_data->get_enc_out_encoding) ++ hdmi->hdmi_data.enc_out_encoding = ++ hdmi->plat_data->get_enc_out_encoding(data); ++ else if ((hdmi->vic == 6) || (hdmi->vic == 7) || ++ (hdmi->vic == 21) || (hdmi->vic == 22) || ++ (hdmi->vic == 2) || (hdmi->vic == 3) || ++ (hdmi->vic == 17) || (hdmi->vic == 18)) ++ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601; ++ else ++ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709; ++ ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK) { ++ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1; ++ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1; ++ } else { ++ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; ++ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; ++ } ++ /* Get input format from plat data or fallback to RGB888 */ ++ if (hdmi->plat_data->get_input_bus_format) ++ hdmi->hdmi_data.enc_in_bus_format = ++ hdmi->plat_data->get_input_bus_format(data); ++ else if (hdmi->plat_data->input_bus_format) ++ hdmi->hdmi_data.enc_in_bus_format = ++ hdmi->plat_data->input_bus_format; ++ else ++ hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ ++ /* Default to RGB888 output format */ ++ if (hdmi->plat_data->get_output_bus_format) ++ hdmi->hdmi_data.enc_out_bus_format = ++ hdmi->plat_data->get_output_bus_format(data); ++ else ++ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ ++ /* Get input encoding from plat data or fallback to none */ ++ if (hdmi->plat_data->get_enc_in_encoding) ++ hdmi->hdmi_data.enc_in_encoding = ++ hdmi->plat_data->get_enc_in_encoding(data); ++ else if (hdmi->plat_data->input_bus_encoding) ++ hdmi->hdmi_data.enc_in_encoding = ++ hdmi->plat_data->input_bus_encoding; ++ else ++ hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT; ++ ++ if (hdmi->plat_data->get_quant_range) ++ hdmi->hdmi_data.quant_range = ++ hdmi->plat_data->get_quant_range(data); ++ else ++ hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT; ++ ++ if (hdmi->plat_data->get_link_cfg) ++ link_cfg = hdmi->plat_data->get_link_cfg(data); ++ else ++ return -EINVAL; ++ ++ hdmi->phy.ops->set_mode(hdmi, hdmi->phy.data, HDMI_MODE_FRL_MASK, ++ link_cfg->frl_mode); ++ ++ /* ++ * According to the dw-hdmi specification 6.4.2 ++ * vp_pr_cd[3:0]: ++ * 0000b: No pixel repetition (pixel sent only once) ++ * 0001b: Pixel sent two times (pixel repeated once) ++ */ ++ hdmi->hdmi_data.pix_repet_factor = ++ (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0; ++ hdmi->hdmi_data.video_mode.mdataenablepolarity = true; ++ ++ vmode->previous_pixelclock = vmode->mpixelclock; ++ //[CC:] no split mode ++ // if (hdmi->plat_data->split_mode) ++ // mode->crtc_clock /= 2; ++ vmode->mpixelclock = mode->crtc_clock * 1000; ++ if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) ++ vmode->mpixelclock *= 2; ++ dev_dbg(hdmi->dev, "final pixclk = %ld\n", vmode->mpixelclock); ++ vmode->previous_tmdsclock = vmode->mtmdsclock; ++ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ vmode->mtmdsclock /= 2; ++ dev_info(hdmi->dev, "final tmdsclk = %d\n", vmode->mtmdsclock); ++ ++ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, &hdmi->previous_mode); ++ if (ret) ++ return ret; ++ ++ if (hdmi->plat_data->set_grf_cfg) ++ hdmi->plat_data->set_grf_cfg(data); ++ ++ /* not for DVI mode */ ++ if (hdmi->sink_is_hdmi) { ++ dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__); ++ hdmi_modb(hdmi, 0, OPMODE_DVI, LINK_CONFIG0); ++ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); ++ if (!link_cfg->frl_mode) { ++ if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK) { ++ drm_scdc_readb(hdmi->ddc, SCDC_SINK_VERSION, &bytes); ++ drm_scdc_writeb(hdmi->ddc, SCDC_SOURCE_VERSION, ++ min_t(u8, bytes, SCDC_MIN_SOURCE_VERSION)); ++ //[CC:] use dw_hdmi_set_high_tmds_clock_ratio() ++ drm_scdc_set_high_tmds_clock_ratio(connector, 1); ++ drm_scdc_set_scrambling(connector, 1); ++ hdmi_writel(hdmi, 1, SCRAMB_CONFIG0); ++ } else { ++ if (dw_hdmi_support_scdc(hdmi, &connector->display_info)) { ++ drm_scdc_set_high_tmds_clock_ratio(connector, 0); ++ drm_scdc_set_scrambling(connector, 0); ++ } ++ hdmi_writel(hdmi, 0, SCRAMB_CONFIG0); ++ } ++ } ++ /* HDMI Initialization Step F - Configure AVI InfoFrame */ ++ hdmi_config_AVI(hdmi, connector, mode); ++ hdmi_config_CVTEM(hdmi); ++ hdmi_config_drm_infoframe(hdmi, connector); ++ hdmi_set_op_mode(hdmi, link_cfg, connector); ++ } else { ++ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); ++ hdmi_modb(hdmi, OPMODE_DVI, OPMODE_DVI, LINK_CONFIG0); ++ dev_info(hdmi->dev, "%s DVI mode\n", __func__); ++ } ++ ++ return 0; ++} ++ ++static enum drm_connector_status ++dw_hdmi_connector_detect(struct drm_connector *connector, bool force) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ struct dw_hdmi_qp *secondary = NULL; ++ enum drm_connector_status result, result_secondary; ++ ++ mutex_lock(&hdmi->mutex); ++ hdmi->force = DRM_FORCE_UNSPECIFIED; ++ mutex_unlock(&hdmi->mutex); ++ ++ if (hdmi->plat_data->left) ++ secondary = hdmi->plat_data->left; ++ else if (hdmi->plat_data->right) ++ secondary = hdmi->plat_data->right; ++ ++ result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); ++ ++ if (secondary) { ++ result_secondary = secondary->phy.ops->read_hpd(secondary, secondary->phy.data); ++ if (result == connector_status_connected && ++ result_secondary == connector_status_connected) ++ result = connector_status_connected; ++ else ++ result = connector_status_disconnected; ++ } ++ ++ mutex_lock(&hdmi->mutex); ++ if (result != hdmi->last_connector_result) { ++ dev_dbg(hdmi->dev, "read_hpd result: %d", result); ++ handle_plugged_change(hdmi, ++ result == connector_status_connected); ++ hdmi->last_connector_result = result; ++ } ++ mutex_unlock(&hdmi->mutex); ++ ++ return result; ++} ++ ++static int ++dw_hdmi_update_hdr_property(struct drm_connector *connector) ++{ ++ struct drm_device *dev = connector->dev; ++ struct dw_hdmi_qp *hdmi = container_of(connector, struct dw_hdmi_qp, ++ connector); ++ void *data = hdmi->plat_data->phy_data; ++ const struct hdr_static_metadata *metadata = ++ &connector->hdr_sink_metadata.hdmi_type1; ++ size_t size = sizeof(*metadata); ++ struct drm_property *property = NULL; ++ struct drm_property_blob *blob; ++ int ret; ++ ++ if (hdmi->plat_data->get_hdr_property) ++ property = hdmi->plat_data->get_hdr_property(data); ++ ++ if (!property) ++ return -EINVAL; ++ ++ if (hdmi->plat_data->get_hdr_blob) ++ blob = hdmi->plat_data->get_hdr_blob(data); ++ else ++ return -EINVAL; ++ ++ ret = drm_property_replace_global_blob(dev, &blob, size, metadata, ++ &connector->base, property); ++ return ret; ++} ++ ++static int dw_hdmi_connector_get_modes(struct drm_connector *connector) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ struct hdr_static_metadata *metedata = ++ &connector->hdr_sink_metadata.hdmi_type1; ++ struct edid *edid; ++ struct drm_display_mode *mode; ++ struct drm_display_info *info = &connector->display_info; ++ // void *data = hdmi->plat_data->phy_data; ++ int i, ret = 0; ++ ++ if (!hdmi->ddc) ++ return 0; ++ ++ memset(metedata, 0, sizeof(*metedata)); ++ edid = drm_get_edid(connector, hdmi->ddc); ++ if (edid) { ++ dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", ++ edid->width_cm, edid->height_cm); ++ ++ hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); ++ drm_connector_update_edid_property(connector, edid); ++ // if (hdmi->plat_data->get_edid_dsc_info) ++ // hdmi->plat_data->get_edid_dsc_info(data, edid); ++ ret = drm_add_edid_modes(connector, edid); ++ dw_hdmi_update_hdr_property(connector); ++ // if (ret > 0 && hdmi->plat_data->split_mode) { ++ // struct dw_hdmi_qp *secondary = NULL; ++ // void *secondary_data; ++ // ++ // if (hdmi->plat_data->left) ++ // secondary = hdmi->plat_data->left; ++ // else if (hdmi->plat_data->right) ++ // secondary = hdmi->plat_data->right; ++ // ++ // if (!secondary) ++ // return -ENOMEM; ++ // secondary_data = secondary->plat_data->phy_data; ++ // ++ // list_for_each_entry(mode, &connector->probed_modes, head) ++ // hdmi->plat_data->convert_to_split_mode(mode); ++ // ++ // secondary->sink_is_hdmi = drm_detect_hdmi_monitor(edid); ++ // if (secondary->plat_data->get_edid_dsc_info) ++ // secondary->plat_data->get_edid_dsc_info(secondary_data, edid); ++ // } ++ kfree(edid); ++ } else { ++ hdmi->sink_is_hdmi = true; ++ ++ if (hdmi->plat_data->split_mode) { ++ if (hdmi->plat_data->left) { ++ hdmi->plat_data->left->sink_is_hdmi = true; ++ } else if (hdmi->plat_data->right) { ++ hdmi->plat_data->right->sink_is_hdmi = true; ++ } ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) { ++ const struct drm_display_mode *ptr = ++ &dw_hdmi_default_modes[i]; ++ ++ mode = drm_mode_duplicate(connector->dev, ptr); ++ if (mode) { ++ if (!i) ++ mode->type = DRM_MODE_TYPE_PREFERRED; ++ drm_mode_probed_add(connector, mode); ++ ret++; ++ } ++ } ++ if (ret > 0 && hdmi->plat_data->split_mode) { ++ struct drm_display_mode *mode; ++ ++ list_for_each_entry(mode, &connector->probed_modes, head) ++ hdmi->plat_data->convert_to_split_mode(mode); ++ } ++ info->edid_hdmi_rgb444_dc_modes = 0; ++ info->hdmi.y420_dc_modes = 0; ++ info->color_formats = 0; ++ ++ dev_info(hdmi->dev, "failed to get edid\n"); ++ } ++ ++ return ret; ++} ++ ++static int ++dw_hdmi_atomic_connector_set_property(struct drm_connector *connector, ++ struct drm_connector_state *state, ++ struct drm_property *property, ++ uint64_t val) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops; ++ ++ if (ops && ops->set_property) ++ return ops->set_property(connector, state, property, ++ val, hdmi->plat_data->phy_data); ++ else ++ return -EINVAL; ++} ++ ++static int ++dw_hdmi_atomic_connector_get_property(struct drm_connector *connector, ++ const struct drm_connector_state *state, ++ struct drm_property *property, ++ uint64_t *val) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops; ++ ++ if (ops && ops->get_property) ++ return ops->get_property(connector, state, property, ++ val, hdmi->plat_data->phy_data); ++ else ++ return -EINVAL; ++} ++ ++static int ++dw_hdmi_connector_set_property(struct drm_connector *connector, ++ struct drm_property *property, uint64_t val) ++{ ++ return dw_hdmi_atomic_connector_set_property(connector, NULL, ++ property, val); ++} ++ ++static void dw_hdmi_attach_properties(struct dw_hdmi_qp *hdmi) ++{ ++ unsigned int color = MEDIA_BUS_FMT_RGB888_1X24; ++ const struct dw_hdmi_property_ops *ops = ++ hdmi->plat_data->property_ops; ++ ++ if (ops && ops->attach_properties) ++ return ops->attach_properties(&hdmi->connector, color, 0, ++ hdmi->plat_data->phy_data); ++} ++ ++static void dw_hdmi_destroy_properties(struct dw_hdmi_qp *hdmi) ++{ ++ const struct dw_hdmi_property_ops *ops = ++ hdmi->plat_data->property_ops; ++ ++ if (ops && ops->destroy_properties) ++ return ops->destroy_properties(&hdmi->connector, ++ hdmi->plat_data->phy_data); ++} ++ ++static struct drm_encoder * ++dw_hdmi_connector_best_encoder(struct drm_connector *connector) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ ++ return hdmi->bridge.encoder; ++} ++ ++static bool dw_hdmi_color_changed(struct drm_connector *connector, ++ struct drm_atomic_state *state) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ void *data = hdmi->plat_data->phy_data; ++ struct drm_connector_state *old_state = ++ drm_atomic_get_old_connector_state(state, connector); ++ struct drm_connector_state *new_state = ++ drm_atomic_get_new_connector_state(state, connector); ++ bool ret = false; ++ ++ if (hdmi->plat_data->get_color_changed) ++ ret = hdmi->plat_data->get_color_changed(data); ++ ++ if (new_state->colorspace != old_state->colorspace) ++ ret = true; ++ ++ return ret; ++} ++ ++static bool hdr_metadata_equal(const struct drm_connector_state *old_state, ++ const struct drm_connector_state *new_state) ++{ ++ struct drm_property_blob *old_blob = old_state->hdr_output_metadata; ++ struct drm_property_blob *new_blob = new_state->hdr_output_metadata; ++ ++ if (!old_blob || !new_blob) ++ return old_blob == new_blob; ++ ++ if (old_blob->length != new_blob->length) ++ return false; ++ ++ return !memcmp(old_blob->data, new_blob->data, old_blob->length); ++} ++ ++static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, ++ struct drm_atomic_state *state) ++{ ++ struct drm_connector_state *old_state = ++ drm_atomic_get_old_connector_state(state, connector); ++ struct drm_connector_state *new_state = ++ drm_atomic_get_new_connector_state(state, connector); ++ struct drm_crtc *crtc = new_state->crtc; ++ struct drm_crtc_state *crtc_state; ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ struct drm_display_mode *mode = NULL; ++ void *data = hdmi->plat_data->phy_data; ++ struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode; ++ ++ if (!crtc) ++ return 0; ++ ++ crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (IS_ERR(crtc_state)) ++ return PTR_ERR(crtc_state); ++ ++ /* ++ * If HDMI is enabled in uboot, it's need to record ++ * drm_display_mode and set phy status to enabled. ++ */ ++ if (!vmode->mpixelclock) { ++ crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (hdmi->plat_data->get_enc_in_encoding) ++ hdmi->hdmi_data.enc_in_encoding = ++ hdmi->plat_data->get_enc_in_encoding(data); ++ if (hdmi->plat_data->get_enc_out_encoding) ++ hdmi->hdmi_data.enc_out_encoding = ++ hdmi->plat_data->get_enc_out_encoding(data); ++ if (hdmi->plat_data->get_input_bus_format) ++ hdmi->hdmi_data.enc_in_bus_format = ++ hdmi->plat_data->get_input_bus_format(data); ++ if (hdmi->plat_data->get_output_bus_format) ++ hdmi->hdmi_data.enc_out_bus_format = ++ hdmi->plat_data->get_output_bus_format(data); ++ ++ mode = &crtc_state->mode; ++ if (hdmi->plat_data->split_mode) { ++ hdmi->plat_data->convert_to_origin_mode(mode); ++ mode->crtc_clock /= 2; ++ } ++ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); ++ vmode->mpixelclock = mode->crtc_clock * 1000; ++ vmode->previous_pixelclock = mode->clock; ++ vmode->previous_tmdsclock = mode->clock; ++ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, ++ vmode->mpixelclock); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ vmode->mtmdsclock /= 2; ++ } ++ ++ if (!hdr_metadata_equal(old_state, new_state) || ++ dw_hdmi_color_changed(connector, state)) { ++ crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (IS_ERR(crtc_state)) ++ return PTR_ERR(crtc_state); ++ ++ crtc_state->mode_changed = true; ++ } ++ ++ return 0; ++} ++ ++static void dw_hdmi_connector_force(struct drm_connector *connector) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ ++ mutex_lock(&hdmi->mutex); ++ ++ if (hdmi->force != connector->force) { ++ if (!hdmi->disabled && connector->force == DRM_FORCE_OFF) ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, ++ false); ++ else if (hdmi->disabled && connector->force == DRM_FORCE_ON) ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, ++ true); ++ } ++ ++ hdmi->force = connector->force; ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static int dw_hdmi_qp_fill_modes(struct drm_connector *connector, u32 max_x, ++ u32 max_y) ++{ ++ return drm_helper_probe_single_connector_modes(connector, 9000, 9000); ++} ++ ++static const struct drm_connector_funcs dw_hdmi_connector_funcs = { ++ .fill_modes = dw_hdmi_qp_fill_modes, ++ .detect = dw_hdmi_connector_detect, ++ .destroy = drm_connector_cleanup, ++ .force = dw_hdmi_connector_force, ++ .reset = drm_atomic_helper_connector_reset, ++ .set_property = dw_hdmi_connector_set_property, ++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, ++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, ++ .atomic_set_property = dw_hdmi_atomic_connector_set_property, ++ .atomic_get_property = dw_hdmi_atomic_connector_get_property, ++}; ++ ++static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { ++ .get_modes = dw_hdmi_connector_get_modes, ++ .best_encoder = dw_hdmi_connector_best_encoder, ++ .atomic_check = dw_hdmi_connector_atomic_check, ++}; ++ ++static int dw_hdmi_qp_bridge_attach(struct drm_bridge *bridge, ++ enum drm_bridge_attach_flags flags) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ struct drm_encoder *encoder = bridge->encoder; ++ struct drm_connector *connector = &hdmi->connector; ++ ++ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) ++ return 0; ++ ++ connector->interlace_allowed = 1; ++ connector->polled = DRM_CONNECTOR_POLL_HPD; ++ ++ drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs); ++ ++ // [CC:] use drm_connector_init_with_ddc or drmm_connector_init ++ // to provide ddc reference ++ drm_connector_init_with_ddc(bridge->dev, connector, ++ &dw_hdmi_connector_funcs, ++ DRM_MODE_CONNECTOR_HDMIA, ++ hdmi->ddc); ++ ++ drm_connector_attach_encoder(connector, encoder); ++ dw_hdmi_attach_properties(hdmi); ++ ++ return 0; ++} ++ ++static void dw_hdmi_qp_bridge_mode_set(struct drm_bridge *bridge, ++ const struct drm_display_mode *orig_mode, ++ const struct drm_display_mode *mode) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ ++ mutex_lock(&hdmi->mutex); ++ ++ /* Store the display mode for plugin/DKMS poweron events */ ++ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); ++ if (hdmi->plat_data->split_mode) ++ hdmi->plat_data->convert_to_origin_mode(&hdmi->previous_mode); ++ ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static enum drm_mode_status ++dw_hdmi_qp_bridge_mode_valid(struct drm_bridge *bridge, ++ const struct drm_display_info *info, ++ const struct drm_display_mode *mode) ++{ ++ return MODE_OK; ++} ++ ++static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, ++ struct drm_bridge_state *old_state) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ struct drm_atomic_state *state = old_state->base.state; ++ struct drm_connector *connector; ++ ++ connector = drm_atomic_get_new_connector_for_encoder(state, ++ bridge->encoder); ++ ++ mutex_lock(&hdmi->mutex); ++ hdmi->curr_conn = connector; ++ dw_hdmi_qp_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); ++ hdmi->disabled = false; ++ mutex_unlock(&hdmi->mutex); ++ ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true); ++ handle_plugged_change(hdmi, true); ++} ++ ++static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, ++ struct drm_bridge_state *old_state) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false); ++ handle_plugged_change(hdmi, false); ++ mutex_lock(&hdmi->mutex); ++ ++ hdmi->curr_conn = NULL; ++ ++ if (hdmi->phy.ops->disable) ++ hdmi->phy.ops->disable(hdmi, hdmi->phy.data); ++ hdmi->disabled = true; ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { ++ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, ++ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, ++ .atomic_reset = drm_atomic_helper_bridge_reset, ++ .attach = dw_hdmi_qp_bridge_attach, ++ .mode_set = dw_hdmi_qp_bridge_mode_set, ++ .mode_valid = dw_hdmi_qp_bridge_mode_valid, ++ .atomic_enable = dw_hdmi_qp_bridge_atomic_enable, ++ .atomic_disable = dw_hdmi_qp_bridge_atomic_disable, ++}; ++ ++static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, MAINUNIT_1_INT_STATUS); ++ ++ i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ | ++ I2CM_NACK_RCVD_IRQ); ++ hdmi->scdc_intr = stat & (SCDC_UPD_FLAGS_RD_IRQ | ++ SCDC_UPD_FLAGS_CHG_IRQ | ++ SCDC_UPD_FLAGS_CLR_IRQ | ++ SCDC_RR_REPLY_STOP_IRQ | ++ SCDC_NACK_RCVD_IRQ); ++ hdmi->flt_intr = stat & (FLT_EXIT_TO_LTSP_IRQ | ++ FLT_EXIT_TO_LTS4_IRQ | ++ FLT_EXIT_TO_LTSL_IRQ); ++ ++ if (i2c->stat) { ++ hdmi_writel(hdmi, i2c->stat, MAINUNIT_1_INT_CLEAR); ++ complete(&i2c->cmp); ++ } ++ ++ if (hdmi->flt_intr) { ++ dev_dbg(hdmi->dev, "i2c flt irq:%#x\n", hdmi->flt_intr); ++ hdmi_writel(hdmi, hdmi->flt_intr, MAINUNIT_1_INT_CLEAR); ++ complete(&hdmi->flt_cmp); ++ } ++ ++ if (hdmi->scdc_intr) { ++ u8 val; ++ ++ dev_dbg(hdmi->dev, "i2c scdc irq:%#x\n", hdmi->scdc_intr); ++ hdmi_writel(hdmi, hdmi->scdc_intr, MAINUNIT_1_INT_CLEAR); ++ val = hdmi_readl(hdmi, SCDC_STATUS0); ++ ++ /* frl start */ ++ if (val & BIT(4)) { ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | ++ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ dev_info(hdmi->dev, "frl start\n"); ++ } ++ ++ } ++ ++ if (stat) ++ return IRQ_HANDLED; ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t dw_hdmi_qp_avp_hardirq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, AVP_1_INT_STATUS); ++ if (stat) { ++ dev_dbg(hdmi->dev, "HDCP irq %#x\n", stat); ++ stat &= ~stat; ++ hdmi_writel(hdmi, stat, AVP_1_INT_MASK_N); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t dw_hdmi_qp_earc_hardirq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, EARCRX_0_INT_STATUS); ++ if (stat) { ++ dev_dbg(hdmi->dev, "earc irq %#x\n", stat); ++ stat &= ~stat; ++ hdmi_writel(hdmi, stat, EARCRX_0_INT_MASK_N); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, AVP_1_INT_STATUS); ++ ++ if (!stat) ++ return IRQ_NONE; ++ ++ hdmi_writel(hdmi, stat, AVP_1_INT_CLEAR); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t dw_hdmi_qp_earc_irq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, EARCRX_0_INT_STATUS); ++ ++ if (!stat) ++ return IRQ_NONE; ++ ++ hdmi_writel(hdmi, stat, EARCRX_0_INT_CLEAR); ++ ++ hdmi->earc_intr = stat; ++ complete(&hdmi->earc_cmp); ++ ++ return IRQ_HANDLED; ++} ++ ++static int dw_hdmi_detect_phy(struct dw_hdmi_qp *hdmi) ++{ ++ u8 phy_type; ++ ++ phy_type = hdmi->plat_data->phy_force_vendor ? ++ DW_HDMI_PHY_VENDOR_PHY : 0; ++ ++ if (phy_type == DW_HDMI_PHY_VENDOR_PHY) { ++ /* Vendor PHYs require support from the glue layer. */ ++ if (!hdmi->plat_data->qp_phy_ops || !hdmi->plat_data->phy_name) { ++ dev_err(hdmi->dev, ++ "Vendor HDMI PHY not supported by glue layer\n"); ++ return -ENODEV; ++ } ++ ++ hdmi->phy.ops = hdmi->plat_data->qp_phy_ops; ++ hdmi->phy.data = hdmi->plat_data->phy_data; ++ hdmi->phy.name = hdmi->plat_data->phy_name; ++ } ++ ++ return 0; ++} ++ ++static const struct regmap_config hdmi_regmap_config = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = EARCRX_1_INT_FORCE, ++}; ++ ++struct dw_hdmi_qp_reg_table { ++ int reg_base; ++ int reg_end; ++}; ++ ++static const struct dw_hdmi_qp_reg_table hdmi_reg_table[] = { ++ {0x0, 0xc}, ++ {0x14, 0x1c}, ++ {0x44, 0x48}, ++ {0x50, 0x58}, ++ {0x80, 0x84}, ++ {0xa0, 0xc4}, ++ {0xe0, 0xe8}, ++ {0xf0, 0x118}, ++ {0x140, 0x140}, ++ {0x150, 0x150}, ++ {0x160, 0x168}, ++ {0x180, 0x180}, ++ {0x800, 0x800}, ++ {0x808, 0x808}, ++ {0x814, 0x814}, ++ {0x81c, 0x824}, ++ {0x834, 0x834}, ++ {0x840, 0x864}, ++ {0x86c, 0x86c}, ++ {0x880, 0x89c}, ++ {0x8e0, 0x8e8}, ++ {0x900, 0x900}, ++ {0x908, 0x90c}, ++ {0x920, 0x938}, ++ {0x920, 0x938}, ++ {0x960, 0x960}, ++ {0x968, 0x968}, ++ {0xa20, 0xa20}, ++ {0xa30, 0xa30}, ++ {0xa40, 0xa40}, ++ {0xa54, 0xa54}, ++ {0xa80, 0xaac}, ++ {0xab4, 0xab8}, ++ {0xb00, 0xcbc}, ++ {0xce0, 0xce0}, ++ {0xd00, 0xddc}, ++ {0xe20, 0xe24}, ++ {0xe40, 0xe44}, ++ {0xe4c, 0xe4c}, ++ {0xe60, 0xe80}, ++ {0xea0, 0xf24}, ++ {0x1004, 0x100c}, ++ {0x1020, 0x1030}, ++ {0x1040, 0x1050}, ++ {0x1060, 0x1068}, ++ {0x1800, 0x1820}, ++ {0x182c, 0x182c}, ++ {0x1840, 0x1940}, ++ {0x1960, 0x1a60}, ++ {0x1b00, 0x1b00}, ++ {0x1c00, 0x1c00}, ++ {0x3000, 0x3000}, ++ {0x3010, 0x3014}, ++ {0x3020, 0x3024}, ++ {0x3800, 0x3800}, ++ {0x3810, 0x3814}, ++ {0x3820, 0x3824}, ++ {0x3830, 0x3834}, ++ {0x3840, 0x3844}, ++ {0x3850, 0x3854}, ++ {0x3860, 0x3864}, ++ {0x3870, 0x3874}, ++ {0x4000, 0x4004}, ++ {0x4800, 0x4800}, ++ {0x4810, 0x4814}, ++}; ++ ++static int dw_hdmi_ctrl_show(struct seq_file *s, void *v) ++{ ++ struct dw_hdmi_qp *hdmi = s->private; ++ u32 i = 0, j = 0, val = 0; ++ ++ seq_puts(s, "\n---------------------------------------------------"); ++ ++ for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) { ++ for (j = hdmi_reg_table[i].reg_base; ++ j <= hdmi_reg_table[i].reg_end; j += 4) { ++ val = hdmi_readl(hdmi, j); ++ ++ if ((j - hdmi_reg_table[i].reg_base) % 16 == 0) ++ seq_printf(s, "\n>>>hdmi_ctl %04x:", j); ++ seq_printf(s, " %08x", val); ++ } ++ } ++ seq_puts(s, "\n---------------------------------------------------\n"); ++ ++ return 0; ++} ++ ++static int dw_hdmi_ctrl_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, dw_hdmi_ctrl_show, inode->i_private); ++} ++ ++static ssize_t ++dw_hdmi_ctrl_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dw_hdmi_qp *hdmi = ++ ((struct seq_file *)file->private_data)->private; ++ u32 reg, val; ++ char kbuf[25]; ++ ++ if (count > 24) { ++ dev_err(hdmi->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 > EARCRX_1_INT_FORCE) { ++ dev_err(hdmi->dev, "it is no a hdmi register\n"); ++ return count; ++ } ++ dev_info(hdmi->dev, "/**********hdmi register config******/"); ++ dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val); ++ hdmi_writel(hdmi, val, reg); ++ return count; ++} ++ ++static const struct file_operations dw_hdmi_ctrl_fops = { ++ .owner = THIS_MODULE, ++ .open = dw_hdmi_ctrl_open, ++ .read = seq_read, ++ .write = dw_hdmi_ctrl_write, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int dw_hdmi_status_show(struct seq_file *s, void *v) ++{ ++ struct dw_hdmi_qp *hdmi = s->private; ++ u32 val; ++ ++ seq_puts(s, "PHY: "); ++ if (hdmi->disabled) { ++ seq_puts(s, "disabled\n"); ++ return 0; ++ } ++ seq_puts(s, "enabled\t\t\tMode: "); ++ if (hdmi->sink_is_hdmi) ++ seq_puts(s, "HDMI\n"); ++ else ++ seq_puts(s, "DVI\n"); ++ ++ if (hdmi->hdmi_data.video_mode.mpixelclock > 600000000) { ++ seq_printf(s, "FRL Mode Pixel Clk: %luHz\n", ++ hdmi->hdmi_data.video_mode.mpixelclock); ++ } else { ++ if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000) ++ val = hdmi->hdmi_data.video_mode.mtmdsclock / 4; ++ else ++ val = hdmi->hdmi_data.video_mode.mtmdsclock; ++ seq_printf(s, "TMDS Mode Pixel Clk: %luHz\t\tTMDS Clk: %uHz\n", ++ hdmi->hdmi_data.video_mode.mpixelclock, val); ++ } ++ ++ seq_puts(s, "Color Format: "); ++ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "RGB"); ++ else if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "YUV444"); ++ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "YUV422"); ++ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "YUV420"); ++ else ++ seq_puts(s, "UNKNOWN"); ++ val = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); ++ seq_printf(s, "\t\tColor Depth: %d bit\n", val); ++ seq_puts(s, "Colorimetry: "); ++ switch (hdmi->hdmi_data.enc_out_encoding) { ++ case V4L2_YCBCR_ENC_601: ++ seq_puts(s, "ITU.BT601"); ++ break; ++ case V4L2_YCBCR_ENC_709: ++ seq_puts(s, "ITU.BT709"); ++ break; ++ case V4L2_YCBCR_ENC_BT2020: ++ seq_puts(s, "ITU.BT2020"); ++ break; ++ default: /* Carries no data */ ++ seq_puts(s, "ITU.BT601"); ++ break; ++ } ++ ++ seq_puts(s, "\t\tEOTF: "); ++ ++ val = hdmi_readl(hdmi, PKTSCHED_PKT_EN); ++ if (!(val & PKTSCHED_DRMI_TX_EN)) { ++ seq_puts(s, "Off\n"); ++ return 0; ++ } ++ ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS1); ++ val = (val >> 8) & 0x7; ++ switch (val) { ++ case HDMI_EOTF_TRADITIONAL_GAMMA_SDR: ++ seq_puts(s, "SDR"); ++ break; ++ case HDMI_EOTF_TRADITIONAL_GAMMA_HDR: ++ seq_puts(s, "HDR"); ++ break; ++ case HDMI_EOTF_SMPTE_ST2084: ++ seq_puts(s, "ST2084"); ++ break; ++ case HDMI_EOTF_BT_2100_HLG: ++ seq_puts(s, "HLG"); ++ break; ++ default: ++ seq_puts(s, "Not Defined\n"); ++ return 0; ++ } ++ ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS1); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "\nx0: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS2); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\t\ty0: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS2); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "x1: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS3); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\t\ty1: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS3); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "x2: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS4); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\t\ty2: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS4); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "white x: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS5); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\twhite y: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS5); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "max lum: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS6); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\tmin lum: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS6); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "max cll: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS7); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\tmax fall: %d\n", val); ++ return 0; ++} ++ ++static int dw_hdmi_status_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, dw_hdmi_status_show, inode->i_private); ++} ++ ++static const struct file_operations dw_hdmi_status_fops = { ++ .owner = THIS_MODULE, ++ .open = dw_hdmi_status_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi_qp *hdmi) ++{ ++ u8 buf[11]; ++ ++ snprintf(buf, sizeof(buf), "dw-hdmi%d", hdmi->plat_data->id); ++ hdmi->debugfs_dir = debugfs_create_dir(buf, NULL); ++ if (IS_ERR(hdmi->debugfs_dir)) { ++ dev_err(dev, "failed to create debugfs dir!\n"); ++ return; ++ } ++ ++ debugfs_create_file("status", 0400, hdmi->debugfs_dir, ++ hdmi, &dw_hdmi_status_fops); ++ debugfs_create_file("ctrl", 0600, hdmi->debugfs_dir, ++ hdmi, &dw_hdmi_ctrl_fops); ++} ++ ++static struct dw_hdmi_qp * ++__dw_hdmi_probe(struct platform_device *pdev, ++ const struct dw_hdmi_plat_data *plat_data) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *ddc_node; ++ struct dw_hdmi_qp *hdmi; ++ struct resource *iores = NULL; ++ int irq; ++ int ret; ++ ++ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); ++ if (!hdmi) ++ return ERR_PTR(-ENOMEM); ++ ++ hdmi->connector.stereo_allowed = 1; ++ hdmi->plat_data = plat_data; ++ hdmi->dev = dev; ++ hdmi->disabled = true; ++ ++ mutex_init(&hdmi->mutex); ++ ++ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); ++ if (ddc_node) { ++ hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node); ++ of_node_put(ddc_node); ++ if (!hdmi->ddc) { ++ dev_dbg(hdmi->dev, "failed to read ddc node\n"); ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ } else { ++ dev_dbg(hdmi->dev, "no ddc property found\n"); ++ } ++ ++ if (!plat_data->regm) { ++ const struct regmap_config *reg_config; ++ ++ reg_config = &hdmi_regmap_config; ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ hdmi->regs = devm_ioremap_resource(dev, iores); ++ if (IS_ERR(hdmi->regs)) { ++ ret = PTR_ERR(hdmi->regs); ++ goto err_res; ++ } ++ ++ hdmi->regm = devm_regmap_init_mmio(dev, hdmi->regs, reg_config); ++ if (IS_ERR(hdmi->regm)) { ++ dev_err(dev, "Failed to configure regmap\n"); ++ ret = PTR_ERR(hdmi->regm); ++ goto err_res; ++ } ++ } else { ++ hdmi->regm = plat_data->regm; ++ } ++ ++ ret = dw_hdmi_detect_phy(hdmi); ++ if (ret < 0) ++ goto err_res; ++ ++ hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N); ++ hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N); ++ hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0); ++ if ((hdmi_readl(hdmi, CMU_STATUS) & DISPLAY_CLK_MONITOR) == DISPLAY_CLK_LOCKED) { ++ hdmi->initialized = true; ++ hdmi->disabled = false; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ hdmi->avp_irq = irq; ++ ret = devm_request_threaded_irq(dev, hdmi->avp_irq, ++ dw_hdmi_qp_avp_hardirq, ++ dw_hdmi_qp_avp_irq, IRQF_SHARED, ++ dev_name(dev), hdmi); ++ if (ret) ++ goto err_res; ++ ++ irq = platform_get_irq(pdev, 1); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ irq = platform_get_irq(pdev, 2); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ hdmi->earc_irq = irq; ++ ret = devm_request_threaded_irq(dev, hdmi->earc_irq, ++ dw_hdmi_qp_earc_hardirq, ++ dw_hdmi_qp_earc_irq, IRQF_SHARED, ++ dev_name(dev), hdmi); ++ if (ret) ++ goto err_res; ++ ++ irq = platform_get_irq(pdev, 3); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ hdmi->main_irq = irq; ++ ret = devm_request_threaded_irq(dev, hdmi->main_irq, ++ dw_hdmi_qp_main_hardirq, NULL, ++ IRQF_SHARED, dev_name(dev), hdmi); ++ if (ret) ++ goto err_res; ++ ++ /* If DDC bus is not specified, try to register HDMI I2C bus */ ++ if (!hdmi->ddc) { ++ hdmi->ddc = dw_hdmi_i2c_adapter(hdmi); ++ if (IS_ERR(hdmi->ddc)) ++ hdmi->ddc = NULL; ++ /* ++ * Read high and low time from device tree. If not available use ++ * the default timing scl clock rate is about 99.6KHz. ++ */ ++ if (of_property_read_u32(np, "ddc-i2c-scl-high-time-ns", ++ &hdmi->i2c->scl_high_ns)) ++ hdmi->i2c->scl_high_ns = 4708; ++ if (of_property_read_u32(np, "ddc-i2c-scl-low-time-ns", ++ &hdmi->i2c->scl_low_ns)) ++ hdmi->i2c->scl_low_ns = 4916; ++ } ++ ++ hdmi->bridge.driver_private = hdmi; ++ hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; ++#ifdef CONFIG_OF ++ hdmi->bridge.of_node = pdev->dev.of_node; ++#endif ++ ++ if (hdmi->phy.ops->setup_hpd) ++ hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); ++ ++ hdmi->connector.ycbcr_420_allowed = hdmi->plat_data->ycbcr_420_allowed; ++ ++ hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, dw_hdmi_cable); ++ if (IS_ERR(hdmi->extcon)) { ++ dev_err(hdmi->dev, "allocate extcon failed\n"); ++ ret = PTR_ERR(hdmi->extcon); ++ goto err_res; ++ } ++ ++ ret = devm_extcon_dev_register(hdmi->dev, hdmi->extcon); ++ if (ret) { ++ dev_err(hdmi->dev, "failed to register extcon: %d\n", ret); ++ goto err_res; ++ } ++ ++ ret = extcon_set_property_capability(hdmi->extcon, EXTCON_DISP_HDMI, ++ EXTCON_PROP_DISP_HPD); ++ if (ret) { ++ dev_err(hdmi->dev, ++ "failed to set USB property capability: %d\n", ret); ++ goto err_res; ++ } ++ ++ /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */ ++ if (hdmi->i2c) ++ dw_hdmi_i2c_init(hdmi); ++ ++ init_completion(&hdmi->flt_cmp); ++ init_completion(&hdmi->earc_cmp); ++ ++ if (of_property_read_bool(np, "scramble-low-rates")) ++ hdmi->scramble_low_rates = true; ++ ++ dw_hdmi_register_debugfs(dev, hdmi); ++ ++ return hdmi; ++ ++err_res: ++ if (hdmi->i2c) ++ i2c_del_adapter(&hdmi->i2c->adap); ++ else ++ i2c_put_adapter(hdmi->ddc); ++ ++ return ERR_PTR(ret); ++} ++ ++static void __dw_hdmi_remove(struct dw_hdmi_qp *hdmi) ++{ ++ if (hdmi->avp_irq) ++ disable_irq(hdmi->avp_irq); ++ ++ if (hdmi->main_irq) ++ disable_irq(hdmi->main_irq); ++ ++ if (hdmi->earc_irq) ++ disable_irq(hdmi->earc_irq); ++ ++ debugfs_remove_recursive(hdmi->debugfs_dir); ++ ++ if (!hdmi->plat_data->first_screen) { ++ dw_hdmi_destroy_properties(hdmi); ++ hdmi->connector.funcs->destroy(&hdmi->connector); ++ } ++ ++ if (hdmi->audio && !IS_ERR(hdmi->audio)) ++ platform_device_unregister(hdmi->audio); ++ ++ // [CC:] dw_hdmi_rockchip_unbind() also calls drm_encoder_cleanup() ++ // and causes a seg fault due to NULL ptr dererence ++ // if (hdmi->bridge.encoder && !hdmi->plat_data->first_screen) ++ // hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder); ++ // ++ if (!IS_ERR(hdmi->cec)) ++ platform_device_unregister(hdmi->cec); ++ if (hdmi->i2c) ++ i2c_del_adapter(&hdmi->i2c->adap); ++ else ++ i2c_put_adapter(hdmi->ddc); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Bind/unbind API, used from platforms based on the component framework. ++ */ ++struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, ++ struct drm_encoder *encoder, ++ struct dw_hdmi_plat_data *plat_data) ++{ ++ struct dw_hdmi_qp *hdmi; ++ int ret; ++ ++ hdmi = __dw_hdmi_probe(pdev, plat_data); ++ if (IS_ERR(hdmi)) ++ return hdmi; ++ ++ if (!plat_data->first_screen) { ++ ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0); ++ if (ret) { ++ __dw_hdmi_remove(hdmi); ++ dev_err(hdmi->dev, "Failed to initialize bridge with drm\n"); ++ return ERR_PTR(ret); ++ } ++ ++ plat_data->connector = &hdmi->connector; ++ } ++ ++ if (plat_data->split_mode && !hdmi->plat_data->first_screen) { ++ struct dw_hdmi_qp *secondary = NULL; ++ ++ if (hdmi->plat_data->left) ++ secondary = hdmi->plat_data->left; ++ else if (hdmi->plat_data->right) ++ secondary = hdmi->plat_data->right; ++ ++ if (!secondary) ++ return ERR_PTR(-ENOMEM); ++ ret = drm_bridge_attach(encoder, &secondary->bridge, &hdmi->bridge, ++ DRM_BRIDGE_ATTACH_NO_CONNECTOR); ++ if (ret) ++ return ERR_PTR(ret); ++ } ++ ++ return hdmi; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind); ++ ++void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi) ++{ ++ __dw_hdmi_remove(hdmi); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_unbind); ++ ++void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi) ++{ ++ if (!hdmi) { ++ dev_warn(dev, "Hdmi has not been initialized\n"); ++ return; ++ } ++ ++ mutex_lock(&hdmi->mutex); ++ ++ /* ++ * When system shutdown, hdmi should be disabled. ++ * When system suspend, dw_hdmi_qp_bridge_disable will disable hdmi first. ++ * To prevent duplicate operation, we should determine whether hdmi ++ * has been disabled. ++ */ ++ if (!hdmi->disabled) ++ hdmi->disabled = true; ++ mutex_unlock(&hdmi->mutex); ++ ++ if (hdmi->avp_irq) ++ disable_irq(hdmi->avp_irq); ++ ++ if (hdmi->main_irq) ++ disable_irq(hdmi->main_irq); ++ ++ if (hdmi->earc_irq) ++ disable_irq(hdmi->earc_irq); ++ ++ pinctrl_pm_select_sleep_state(dev); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_suspend); ++ ++void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi) ++{ ++ if (!hdmi) { ++ dev_warn(dev, "Hdmi has not been initialized\n"); ++ return; ++ } ++ ++ hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N); ++ hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N); ++ hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0); ++ ++ pinctrl_pm_select_default_state(dev); ++ ++ mutex_lock(&hdmi->mutex); ++ if (hdmi->i2c) ++ dw_hdmi_i2c_init(hdmi); ++ if (hdmi->avp_irq) ++ enable_irq(hdmi->avp_irq); ++ ++ if (hdmi->main_irq) ++ enable_irq(hdmi->main_irq); ++ ++ if (hdmi->earc_irq) ++ enable_irq(hdmi->earc_irq); ++ ++ mutex_unlock(&hdmi->mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_resume); ++ ++MODULE_AUTHOR("Algea Cao "); ++MODULE_DESCRIPTION("DW HDMI QP transmitter driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:dw-hdmi-qp"); +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h +new file mode 100644 +index 000000000000..4cac70f2d11d +--- /dev/null ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h +@@ -0,0 +1,831 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) Rockchip Electronics Co.Ltd ++ * Author: ++ * Algea Cao ++ */ ++#ifndef __DW_HDMI_QP_H__ ++#define __DW_HDMI_QP_H__ ++/* Main Unit Registers */ ++#define CORE_ID 0x0 ++#define VER_NUMBER 0x4 ++#define VER_TYPE 0x8 ++#define CONFIG_REG 0xc ++#define CONFIG_CEC BIT(28) ++#define CONFIG_AUD_UD BIT(23) ++#define CORE_TIMESTAMP_HHMM 0x14 ++#define CORE_TIMESTAMP_MMDD 0x18 ++#define CORE_TIMESTAMP_YYYY 0x1c ++/* Reset Manager Registers */ ++#define GLOBAL_SWRESET_REQUEST 0x40 ++#define EARCRX_CMDC_SWINIT_P BIT(27) ++#define AVP_DATAPATH_PACKET_AUDIO_SWINIT_P BIT(10) ++#define GLOBAL_SWDISABLE 0x44 ++#define CEC_SWDISABLE BIT(17) ++#define AVP_DATAPATH_PACKET_AUDIO_SWDISABLE BIT(10) ++#define AVP_DATAPATH_VIDEO_SWDISABLE BIT(6) ++#define RESET_MANAGER_CONFIG0 0x48 ++#define RESET_MANAGER_STATUS0 0x50 ++#define RESET_MANAGER_STATUS1 0x54 ++#define RESET_MANAGER_STATUS2 0x58 ++/* Timer Base Registers */ ++#define TIMER_BASE_CONFIG0 0x80 ++#define TIMER_BASE_STATUS0 0x84 ++/* CMU Registers */ ++#define CMU_CONFIG0 0xa0 ++#define CMU_CONFIG1 0xa4 ++#define CMU_CONFIG2 0xa8 ++#define CMU_CONFIG3 0xac ++#define CMU_STATUS 0xb0 ++#define DISPLAY_CLK_MONITOR 0x3f ++#define DISPLAY_CLK_LOCKED 0X15 ++#define EARC_BPCLK_OFF BIT(9) ++#define AUDCLK_OFF BIT(7) ++#define LINKQPCLK_OFF BIT(5) ++#define VIDQPCLK_OFF BIT(3) ++#define IPI_CLK_OFF BIT(1) ++#define CMU_IPI_CLK_FREQ 0xb4 ++#define CMU_VIDQPCLK_FREQ 0xb8 ++#define CMU_LINKQPCLK_FREQ 0xbc ++#define CMU_AUDQPCLK_FREQ 0xc0 ++#define CMU_EARC_BPCLK_FREQ 0xc4 ++/* I2CM Registers */ ++#define I2CM_SM_SCL_CONFIG0 0xe0 ++#define I2CM_FM_SCL_CONFIG0 0xe4 ++#define I2CM_CONFIG0 0xe8 ++#define I2CM_CONTROL0 0xec ++#define I2CM_STATUS0 0xf0 ++#define I2CM_INTERFACE_CONTROL0 0xf4 ++#define I2CM_ADDR 0xff000 ++#define I2CM_SLVADDR 0xfe0 ++#define I2CM_WR_MASK 0x1e ++#define I2CM_EXT_READ BIT(4) ++#define I2CM_SHORT_READ BIT(3) ++#define I2CM_FM_READ BIT(2) ++#define I2CM_FM_WRITE BIT(1) ++#define I2CM_FM_EN BIT(0) ++#define I2CM_INTERFACE_CONTROL1 0xf8 ++#define I2CM_SEG_PTR 0x7f80 ++#define I2CM_SEG_ADDR 0x7f ++#define I2CM_INTERFACE_WRDATA_0_3 0xfc ++#define I2CM_INTERFACE_WRDATA_4_7 0x100 ++#define I2CM_INTERFACE_WRDATA_8_11 0x104 ++#define I2CM_INTERFACE_WRDATA_12_15 0x108 ++#define I2CM_INTERFACE_RDDATA_0_3 0x10c ++#define I2CM_INTERFACE_RDDATA_4_7 0x110 ++#define I2CM_INTERFACE_RDDATA_8_11 0x114 ++#define I2CM_INTERFACE_RDDATA_12_15 0x118 ++/* SCDC Registers */ ++#define SCDC_CONFIG0 0x140 ++#define SCDC_I2C_FM_EN BIT(12) ++#define SCDC_UPD_FLAGS_AUTO_CLR BIT(6) ++#define SCDC_UPD_FLAGS_POLL_EN BIT(4) ++#define SCDC_CONTROL0 0x148 ++#define SCDC_STATUS0 0x150 ++#define STATUS_UPDATE BIT(0) ++#define FRL_START BIT(4) ++#define FLT_UPDATE BIT(5) ++/* FLT Registers */ ++#define FLT_CONFIG0 0x160 ++#define FLT_CONFIG1 0x164 ++#define FLT_CONFIG2 0x168 ++#define FLT_CONTROL0 0x170 ++/* Main Unit 2 Registers */ ++#define MAINUNIT_STATUS0 0x180 ++/* Video Interface Registers */ ++#define VIDEO_INTERFACE_CONFIG0 0x800 ++#define VIDEO_INTERFACE_CONFIG1 0x804 ++#define VIDEO_INTERFACE_CONFIG2 0x808 ++#define VIDEO_INTERFACE_CONTROL0 0x80c ++#define VIDEO_INTERFACE_STATUS0 0x814 ++/* Video Packing Registers */ ++#define VIDEO_PACKING_CONFIG0 0x81c ++/* Audio Interface Registers */ ++#define AUDIO_INTERFACE_CONFIG0 0x820 ++#define AUD_IF_SEL_MSK 0x3 ++#define AUD_IF_SPDIF 0x2 ++#define AUD_IF_I2S 0x1 ++#define AUD_IF_PAI 0x0 ++#define AUD_FIFO_INIT_ON_OVF_MSK BIT(2) ++#define AUD_FIFO_INIT_ON_OVF_EN BIT(2) ++#define I2S_LINES_EN_MSK GENMASK(7, 4) ++#define I2S_LINES_EN(x) BIT(x + 4) ++#define I2S_BPCUV_RCV_MSK BIT(12) ++#define I2S_BPCUV_RCV_EN BIT(12) ++#define I2S_BPCUV_RCV_DIS 0 ++#define SPDIF_LINES_EN GENMASK(19, 16) ++#define AUD_FORMAT_MSK GENMASK(26, 24) ++#define AUD_3DOBA (0x7 << 24) ++#define AUD_3DASP (0x6 << 24) ++#define AUD_MSOBA (0x5 << 24) ++#define AUD_MSASP (0x4 << 24) ++#define AUD_HBR (0x3 << 24) ++#define AUD_DST (0x2 << 24) ++#define AUD_OBA (0x1 << 24) ++#define AUD_ASP (0x0 << 24) ++#define AUDIO_INTERFACE_CONFIG1 0x824 ++#define AUDIO_INTERFACE_CONTROL0 0x82c ++#define AUDIO_FIFO_CLR_P BIT(0) ++#define AUDIO_INTERFACE_STATUS0 0x834 ++/* Frame Composer Registers */ ++#define FRAME_COMPOSER_CONFIG0 0x840 ++#define FRAME_COMPOSER_CONFIG1 0x844 ++#define FRAME_COMPOSER_CONFIG2 0x848 ++#define FRAME_COMPOSER_CONFIG3 0x84c ++#define FRAME_COMPOSER_CONFIG4 0x850 ++#define FRAME_COMPOSER_CONFIG5 0x854 ++#define FRAME_COMPOSER_CONFIG6 0x858 ++#define FRAME_COMPOSER_CONFIG7 0x85c ++#define FRAME_COMPOSER_CONFIG8 0x860 ++#define FRAME_COMPOSER_CONFIG9 0x864 ++#define FRAME_COMPOSER_CONTROL0 0x86c ++/* Video Monitor Registers */ ++#define VIDEO_MONITOR_CONFIG0 0x880 ++#define VIDEO_MONITOR_STATUS0 0x884 ++#define VIDEO_MONITOR_STATUS1 0x888 ++#define VIDEO_MONITOR_STATUS2 0x88c ++#define VIDEO_MONITOR_STATUS3 0x890 ++#define VIDEO_MONITOR_STATUS4 0x894 ++#define VIDEO_MONITOR_STATUS5 0x898 ++#define VIDEO_MONITOR_STATUS6 0x89c ++/* HDCP2 Logic Registers */ ++#define HDCP2LOGIC_CONFIG0 0x8e0 ++#define HDCP2_BYPASS BIT(0) ++#define HDCP2LOGIC_ESM_GPIO_IN 0x8e4 ++#define HDCP2LOGIC_ESM_GPIO_OUT 0x8e8 ++/* HDCP14 Registers */ ++#define HDCP14_CONFIG0 0x900 ++#define HDCP14_CONFIG1 0x904 ++#define HDCP14_CONFIG2 0x908 ++#define HDCP14_CONFIG3 0x90c ++#define HDCP14_KEY_SEED 0x914 ++#define HDCP14_KEY_H 0x918 ++#define HDCP14_KEY_L 0x91c ++#define HDCP14_KEY_STATUS 0x920 ++#define HDCP14_AKSV_H 0x924 ++#define HDCP14_AKSV_L 0x928 ++#define HDCP14_AN_H 0x92c ++#define HDCP14_AN_L 0x930 ++#define HDCP14_STATUS0 0x934 ++#define HDCP14_STATUS1 0x938 ++/* Scrambler Registers */ ++#define SCRAMB_CONFIG0 0x960 ++/* Video Configuration Registers */ ++#define LINK_CONFIG0 0x968 ++#define OPMODE_FRL_4LANES BIT(8) ++#define OPMODE_DVI BIT(4) ++#define OPMODE_FRL BIT(0) ++/* TMDS FIFO Registers */ ++#define TMDS_FIFO_CONFIG0 0x970 ++#define TMDS_FIFO_CONTROL0 0x974 ++/* FRL RSFEC Registers */ ++#define FRL_RSFEC_CONFIG0 0xa20 ++#define FRL_RSFEC_STATUS0 0xa30 ++/* FRL Packetizer Registers */ ++#define FRL_PKTZ_CONFIG0 0xa40 ++#define FRL_PKTZ_CONTROL0 0xa44 ++#define FRL_PKTZ_CONTROL1 0xa50 ++#define FRL_PKTZ_STATUS1 0xa54 ++/* Packet Scheduler Registers */ ++#define PKTSCHED_CONFIG0 0xa80 ++#define PKTSCHED_PRQUEUE0_CONFIG0 0xa84 ++#define PKTSCHED_PRQUEUE1_CONFIG0 0xa88 ++#define PKTSCHED_PRQUEUE2_CONFIG0 0xa8c ++#define PKTSCHED_PRQUEUE2_CONFIG1 0xa90 ++#define PKTSCHED_PRQUEUE2_CONFIG2 0xa94 ++#define PKTSCHED_PKT_CONFIG0 0xa98 ++#define PKTSCHED_PKT_CONFIG1 0xa9c ++#define PKTSCHED_DRMI_FIELDRATE BIT(13) ++#define PKTSCHED_AVI_FIELDRATE BIT(12) ++#define PKTSCHED_PKT_CONFIG2 0xaa0 ++#define PKTSCHED_PKT_CONFIG3 0xaa4 ++#define PKTSCHED_PKT_EN 0xaa8 ++#define PKTSCHED_DRMI_TX_EN BIT(17) ++#define PKTSCHED_AUDI_TX_EN BIT(15) ++#define PKTSCHED_AVI_TX_EN BIT(13) ++#define PKTSCHED_EMP_CVTEM_TX_EN BIT(10) ++#define PKTSCHED_AMD_TX_EN BIT(8) ++#define PKTSCHED_GCP_TX_EN BIT(3) ++#define PKTSCHED_AUDS_TX_EN BIT(2) ++#define PKTSCHED_ACR_TX_EN BIT(1) ++#define PKTSCHED_NULL_TX_EN BIT(0) ++#define PKTSCHED_PKT_CONTROL0 0xaac ++#define PKTSCHED_PKT_SEND 0xab0 ++#define PKTSCHED_PKT_STATUS0 0xab4 ++#define PKTSCHED_PKT_STATUS1 0xab8 ++#define PKT_NULL_CONTENTS0 0xb00 ++#define PKT_NULL_CONTENTS1 0xb04 ++#define PKT_NULL_CONTENTS2 0xb08 ++#define PKT_NULL_CONTENTS3 0xb0c ++#define PKT_NULL_CONTENTS4 0xb10 ++#define PKT_NULL_CONTENTS5 0xb14 ++#define PKT_NULL_CONTENTS6 0xb18 ++#define PKT_NULL_CONTENTS7 0xb1c ++#define PKT_ACP_CONTENTS0 0xb20 ++#define PKT_ACP_CONTENTS1 0xb24 ++#define PKT_ACP_CONTENTS2 0xb28 ++#define PKT_ACP_CONTENTS3 0xb2c ++#define PKT_ACP_CONTENTS4 0xb30 ++#define PKT_ACP_CONTENTS5 0xb34 ++#define PKT_ACP_CONTENTS6 0xb38 ++#define PKT_ACP_CONTENTS7 0xb3c ++#define PKT_ISRC1_CONTENTS0 0xb40 ++#define PKT_ISRC1_CONTENTS1 0xb44 ++#define PKT_ISRC1_CONTENTS2 0xb48 ++#define PKT_ISRC1_CONTENTS3 0xb4c ++#define PKT_ISRC1_CONTENTS4 0xb50 ++#define PKT_ISRC1_CONTENTS5 0xb54 ++#define PKT_ISRC1_CONTENTS6 0xb58 ++#define PKT_ISRC1_CONTENTS7 0xb5c ++#define PKT_ISRC2_CONTENTS0 0xb60 ++#define PKT_ISRC2_CONTENTS1 0xb64 ++#define PKT_ISRC2_CONTENTS2 0xb68 ++#define PKT_ISRC2_CONTENTS3 0xb6c ++#define PKT_ISRC2_CONTENTS4 0xb70 ++#define PKT_ISRC2_CONTENTS5 0xb74 ++#define PKT_ISRC2_CONTENTS6 0xb78 ++#define PKT_ISRC2_CONTENTS7 0xb7c ++#define PKT_GMD_CONTENTS0 0xb80 ++#define PKT_GMD_CONTENTS1 0xb84 ++#define PKT_GMD_CONTENTS2 0xb88 ++#define PKT_GMD_CONTENTS3 0xb8c ++#define PKT_GMD_CONTENTS4 0xb90 ++#define PKT_GMD_CONTENTS5 0xb94 ++#define PKT_GMD_CONTENTS6 0xb98 ++#define PKT_GMD_CONTENTS7 0xb9c ++#define PKT_AMD_CONTENTS0 0xba0 ++#define PKT_AMD_CONTENTS1 0xba4 ++#define PKT_AMD_CONTENTS2 0xba8 ++#define PKT_AMD_CONTENTS3 0xbac ++#define PKT_AMD_CONTENTS4 0xbb0 ++#define PKT_AMD_CONTENTS5 0xbb4 ++#define PKT_AMD_CONTENTS6 0xbb8 ++#define PKT_AMD_CONTENTS7 0xbbc ++#define PKT_VSI_CONTENTS0 0xbc0 ++#define PKT_VSI_CONTENTS1 0xbc4 ++#define PKT_VSI_CONTENTS2 0xbc8 ++#define PKT_VSI_CONTENTS3 0xbcc ++#define PKT_VSI_CONTENTS4 0xbd0 ++#define PKT_VSI_CONTENTS5 0xbd4 ++#define PKT_VSI_CONTENTS6 0xbd8 ++#define PKT_VSI_CONTENTS7 0xbdc ++#define PKT_AVI_CONTENTS0 0xbe0 ++#define HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT BIT(4) ++#define HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR 0x04 ++#define HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR 0x08 ++#define HDMI_FC_AVICONF2_IT_CONTENT_VALID 0x80 ++#define PKT_AVI_CONTENTS1 0xbe4 ++#define PKT_AVI_CONTENTS2 0xbe8 ++#define PKT_AVI_CONTENTS3 0xbec ++#define PKT_AVI_CONTENTS4 0xbf0 ++#define PKT_AVI_CONTENTS5 0xbf4 ++#define PKT_AVI_CONTENTS6 0xbf8 ++#define PKT_AVI_CONTENTS7 0xbfc ++#define PKT_SPDI_CONTENTS0 0xc00 ++#define PKT_SPDI_CONTENTS1 0xc04 ++#define PKT_SPDI_CONTENTS2 0xc08 ++#define PKT_SPDI_CONTENTS3 0xc0c ++#define PKT_SPDI_CONTENTS4 0xc10 ++#define PKT_SPDI_CONTENTS5 0xc14 ++#define PKT_SPDI_CONTENTS6 0xc18 ++#define PKT_SPDI_CONTENTS7 0xc1c ++#define PKT_AUDI_CONTENTS0 0xc20 ++#define PKT_AUDI_CONTENTS1 0xc24 ++#define PKT_AUDI_CONTENTS2 0xc28 ++#define PKT_AUDI_CONTENTS3 0xc2c ++#define PKT_AUDI_CONTENTS4 0xc30 ++#define PKT_AUDI_CONTENTS5 0xc34 ++#define PKT_AUDI_CONTENTS6 0xc38 ++#define PKT_AUDI_CONTENTS7 0xc3c ++#define PKT_NVI_CONTENTS0 0xc40 ++#define PKT_NVI_CONTENTS1 0xc44 ++#define PKT_NVI_CONTENTS2 0xc48 ++#define PKT_NVI_CONTENTS3 0xc4c ++#define PKT_NVI_CONTENTS4 0xc50 ++#define PKT_NVI_CONTENTS5 0xc54 ++#define PKT_NVI_CONTENTS6 0xc58 ++#define PKT_NVI_CONTENTS7 0xc5c ++#define PKT_DRMI_CONTENTS0 0xc60 ++#define PKT_DRMI_CONTENTS1 0xc64 ++#define PKT_DRMI_CONTENTS2 0xc68 ++#define PKT_DRMI_CONTENTS3 0xc6c ++#define PKT_DRMI_CONTENTS4 0xc70 ++#define PKT_DRMI_CONTENTS5 0xc74 ++#define PKT_DRMI_CONTENTS6 0xc78 ++#define PKT_DRMI_CONTENTS7 0xc7c ++#define PKT_GHDMI1_CONTENTS0 0xc80 ++#define PKT_GHDMI1_CONTENTS1 0xc84 ++#define PKT_GHDMI1_CONTENTS2 0xc88 ++#define PKT_GHDMI1_CONTENTS3 0xc8c ++#define PKT_GHDMI1_CONTENTS4 0xc90 ++#define PKT_GHDMI1_CONTENTS5 0xc94 ++#define PKT_GHDMI1_CONTENTS6 0xc98 ++#define PKT_GHDMI1_CONTENTS7 0xc9c ++#define PKT_GHDMI2_CONTENTS0 0xca0 ++#define PKT_GHDMI2_CONTENTS1 0xca4 ++#define PKT_GHDMI2_CONTENTS2 0xca8 ++#define PKT_GHDMI2_CONTENTS3 0xcac ++#define PKT_GHDMI2_CONTENTS4 0xcb0 ++#define PKT_GHDMI2_CONTENTS5 0xcb4 ++#define PKT_GHDMI2_CONTENTS6 0xcb8 ++#define PKT_GHDMI2_CONTENTS7 0xcbc ++/* EMP Packetizer Registers */ ++#define PKT_EMP_CONFIG0 0xce0 ++#define PKT_EMP_CONTROL0 0xcec ++#define PKT_EMP_CONTROL1 0xcf0 ++#define PKT_EMP_CONTROL2 0xcf4 ++#define PKT_EMP_VTEM_CONTENTS0 0xd00 ++#define PKT_EMP_VTEM_CONTENTS1 0xd04 ++#define PKT_EMP_VTEM_CONTENTS2 0xd08 ++#define PKT_EMP_VTEM_CONTENTS3 0xd0c ++#define PKT_EMP_VTEM_CONTENTS4 0xd10 ++#define PKT_EMP_VTEM_CONTENTS5 0xd14 ++#define PKT_EMP_VTEM_CONTENTS6 0xd18 ++#define PKT_EMP_VTEM_CONTENTS7 0xd1c ++#define PKT0_EMP_CVTEM_CONTENTS0 0xd20 ++#define PKT0_EMP_CVTEM_CONTENTS1 0xd24 ++#define PKT0_EMP_CVTEM_CONTENTS2 0xd28 ++#define PKT0_EMP_CVTEM_CONTENTS3 0xd2c ++#define PKT0_EMP_CVTEM_CONTENTS4 0xd30 ++#define PKT0_EMP_CVTEM_CONTENTS5 0xd34 ++#define PKT0_EMP_CVTEM_CONTENTS6 0xd38 ++#define PKT0_EMP_CVTEM_CONTENTS7 0xd3c ++#define PKT1_EMP_CVTEM_CONTENTS0 0xd40 ++#define PKT1_EMP_CVTEM_CONTENTS1 0xd44 ++#define PKT1_EMP_CVTEM_CONTENTS2 0xd48 ++#define PKT1_EMP_CVTEM_CONTENTS3 0xd4c ++#define PKT1_EMP_CVTEM_CONTENTS4 0xd50 ++#define PKT1_EMP_CVTEM_CONTENTS5 0xd54 ++#define PKT1_EMP_CVTEM_CONTENTS6 0xd58 ++#define PKT1_EMP_CVTEM_CONTENTS7 0xd5c ++#define PKT2_EMP_CVTEM_CONTENTS0 0xd60 ++#define PKT2_EMP_CVTEM_CONTENTS1 0xd64 ++#define PKT2_EMP_CVTEM_CONTENTS2 0xd68 ++#define PKT2_EMP_CVTEM_CONTENTS3 0xd6c ++#define PKT2_EMP_CVTEM_CONTENTS4 0xd70 ++#define PKT2_EMP_CVTEM_CONTENTS5 0xd74 ++#define PKT2_EMP_CVTEM_CONTENTS6 0xd78 ++#define PKT2_EMP_CVTEM_CONTENTS7 0xd7c ++#define PKT3_EMP_CVTEM_CONTENTS0 0xd80 ++#define PKT3_EMP_CVTEM_CONTENTS1 0xd84 ++#define PKT3_EMP_CVTEM_CONTENTS2 0xd88 ++#define PKT3_EMP_CVTEM_CONTENTS3 0xd8c ++#define PKT3_EMP_CVTEM_CONTENTS4 0xd90 ++#define PKT3_EMP_CVTEM_CONTENTS5 0xd94 ++#define PKT3_EMP_CVTEM_CONTENTS6 0xd98 ++#define PKT3_EMP_CVTEM_CONTENTS7 0xd9c ++#define PKT4_EMP_CVTEM_CONTENTS0 0xda0 ++#define PKT4_EMP_CVTEM_CONTENTS1 0xda4 ++#define PKT4_EMP_CVTEM_CONTENTS2 0xda8 ++#define PKT4_EMP_CVTEM_CONTENTS3 0xdac ++#define PKT4_EMP_CVTEM_CONTENTS4 0xdb0 ++#define PKT4_EMP_CVTEM_CONTENTS5 0xdb4 ++#define PKT4_EMP_CVTEM_CONTENTS6 0xdb8 ++#define PKT4_EMP_CVTEM_CONTENTS7 0xdbc ++#define PKT5_EMP_CVTEM_CONTENTS0 0xdc0 ++#define PKT5_EMP_CVTEM_CONTENTS1 0xdc4 ++#define PKT5_EMP_CVTEM_CONTENTS2 0xdc8 ++#define PKT5_EMP_CVTEM_CONTENTS3 0xdcc ++#define PKT5_EMP_CVTEM_CONTENTS4 0xdd0 ++#define PKT5_EMP_CVTEM_CONTENTS5 0xdd4 ++#define PKT5_EMP_CVTEM_CONTENTS6 0xdd8 ++#define PKT5_EMP_CVTEM_CONTENTS7 0xddc ++/* Audio Packetizer Registers */ ++#define AUDPKT_CONTROL0 0xe20 ++#define AUDPKT_PBIT_FORCE_EN_MASK BIT(12) ++#define AUDPKT_PBIT_FORCE_EN BIT(12) ++#define AUDPKT_CHSTATUS_OVR_EN_MASK BIT(0) ++#define AUDPKT_CHSTATUS_OVR_EN BIT(0) ++#define AUDPKT_CONTROL1 0xe24 ++#define AUDPKT_ACR_CONTROL0 0xe40 ++#define AUDPKT_ACR_N_VALUE 0xfffff ++#define AUDPKT_ACR_CONTROL1 0xe44 ++#define AUDPKT_ACR_CTS_OVR_VAL_MSK GENMASK(23, 4) ++#define AUDPKT_ACR_CTS_OVR_VAL(x) ((x) << 4) ++#define AUDPKT_ACR_CTS_OVR_EN_MSK BIT(1) ++#define AUDPKT_ACR_CTS_OVR_EN BIT(1) ++#define AUDPKT_ACR_STATUS0 0xe4c ++#define AUDPKT_CHSTATUS_OVR0 0xe60 ++#define AUDPKT_CHSTATUS_OVR1 0xe64 ++/* IEC60958 Byte 3: Sampleing frenuency Bits 24 to 27 */ ++#define AUDPKT_CHSTATUS_SR_MASK GENMASK(3, 0) ++#define AUDPKT_CHSTATUS_SR_22050 0x4 ++#define AUDPKT_CHSTATUS_SR_24000 0x6 ++#define AUDPKT_CHSTATUS_SR_32000 0x3 ++#define AUDPKT_CHSTATUS_SR_44100 0x0 ++#define AUDPKT_CHSTATUS_SR_48000 0x2 ++#define AUDPKT_CHSTATUS_SR_88200 0x8 ++#define AUDPKT_CHSTATUS_SR_96000 0xa ++#define AUDPKT_CHSTATUS_SR_176400 0xc ++#define AUDPKT_CHSTATUS_SR_192000 0xe ++#define AUDPKT_CHSTATUS_SR_768000 0x9 ++#define AUDPKT_CHSTATUS_SR_NOT_INDICATED 0x1 ++/* IEC60958 Byte 4: Original Sampleing frenuency Bits 36 to 39 */ ++#define AUDPKT_CHSTATUS_0SR_MASK GENMASK(15, 12) ++#define AUDPKT_CHSTATUS_OSR_8000 0x6 ++#define AUDPKT_CHSTATUS_OSR_11025 0xa ++#define AUDPKT_CHSTATUS_OSR_12000 0x2 ++#define AUDPKT_CHSTATUS_OSR_16000 0x8 ++#define AUDPKT_CHSTATUS_OSR_22050 0xb ++#define AUDPKT_CHSTATUS_OSR_24000 0x9 ++#define AUDPKT_CHSTATUS_OSR_32000 0xc ++#define AUDPKT_CHSTATUS_OSR_44100 0xf ++#define AUDPKT_CHSTATUS_OSR_48000 0xd ++#define AUDPKT_CHSTATUS_OSR_88200 0x7 ++#define AUDPKT_CHSTATUS_OSR_96000 0x5 ++#define AUDPKT_CHSTATUS_OSR_176400 0x3 ++#define AUDPKT_CHSTATUS_OSR_192000 0x1 ++#define AUDPKT_CHSTATUS_OSR_NOT_INDICATED 0x0 ++#define AUDPKT_CHSTATUS_OVR2 0xe68 ++#define AUDPKT_CHSTATUS_OVR3 0xe6c ++#define AUDPKT_CHSTATUS_OVR4 0xe70 ++#define AUDPKT_CHSTATUS_OVR5 0xe74 ++#define AUDPKT_CHSTATUS_OVR6 0xe78 ++#define AUDPKT_CHSTATUS_OVR7 0xe7c ++#define AUDPKT_CHSTATUS_OVR8 0xe80 ++#define AUDPKT_CHSTATUS_OVR9 0xe84 ++#define AUDPKT_CHSTATUS_OVR10 0xe88 ++#define AUDPKT_CHSTATUS_OVR11 0xe8c ++#define AUDPKT_CHSTATUS_OVR12 0xe90 ++#define AUDPKT_CHSTATUS_OVR13 0xe94 ++#define AUDPKT_CHSTATUS_OVR14 0xe98 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC0 0xea0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC1 0xea4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC2 0xea8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC3 0xeac ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC4 0xeb0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC5 0xeb4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC6 0xeb8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC7 0xebc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC8 0xec0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC9 0xec4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC10 0xec8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC11 0xecc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC12 0xed0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC13 0xed4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC14 0xed8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC15 0xedc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC16 0xee0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC17 0xee4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC18 0xee8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC19 0xeec ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC20 0xef0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC21 0xef4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC22 0xef8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC23 0xefc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC24 0xf00 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC25 0xf04 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC26 0xf08 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC27 0xf0c ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC28 0xf10 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC29 0xf14 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC30 0xf18 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC31 0xf1c ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC32 0xf20 ++#define AUDPKT_VBIT_OVR0 0xf24 ++/* CEC Registers */ ++#define CEC_TX_CONTROL 0x1000 ++#define CEC_STATUS 0x1004 ++#define CEC_CONFIG 0x1008 ++#define CEC_ADDR 0x100c ++#define CEC_TX_COUNT 0x1020 ++#define CEC_TX_DATA3_0 0x1024 ++#define CEC_TX_DATA7_4 0x1028 ++#define CEC_TX_DATA11_8 0x102c ++#define CEC_TX_DATA15_12 0x1030 ++#define CEC_RX_COUNT_STATUS 0x1040 ++#define CEC_RX_DATA3_0 0x1044 ++#define CEC_RX_DATA7_4 0x1048 ++#define CEC_RX_DATA11_8 0x104c ++#define CEC_RX_DATA15_12 0x1050 ++#define CEC_LOCK_CONTROL 0x1054 ++#define CEC_RXQUAL_BITTIME_CONFIG 0x1060 ++#define CEC_RX_BITTIME_CONFIG 0x1064 ++#define CEC_TX_BITTIME_CONFIG 0x1068 ++/* eARC RX CMDC Registers */ ++#define EARCRX_CMDC_CONFIG0 0x1800 ++#define EARCRX_XACTREAD_STOP_CFG BIT(26) ++#define EARCRX_XACTREAD_RETRY_CFG BIT(25) ++#define EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 BIT(24) ++#define EARCRX_CMDC_XACT_RESTART_EN BIT(18) ++#define EARCRX_CMDC_CONFIG1 0x1804 ++#define EARCRX_CMDC_CONTROL 0x1808 ++#define EARCRX_CMDC_HEARTBEAT_LOSS_EN BIT(4) ++#define EARCRX_CMDC_DISCOVERY_EN BIT(3) ++#define EARCRX_CONNECTOR_HPD BIT(1) ++#define EARCRX_CMDC_WHITELIST0_CONFIG 0x180c ++#define EARCRX_CMDC_WHITELIST1_CONFIG 0x1810 ++#define EARCRX_CMDC_WHITELIST2_CONFIG 0x1814 ++#define EARCRX_CMDC_WHITELIST3_CONFIG 0x1818 ++#define EARCRX_CMDC_STATUS 0x181c ++#define EARCRX_CMDC_XACT_INFO 0x1820 ++#define EARCRX_CMDC_XACT_ACTION 0x1824 ++#define EARCRX_CMDC_HEARTBEAT_RXSTAT_SE 0x1828 ++#define EARCRX_CMDC_HEARTBEAT_STATUS 0x182c ++#define EARCRX_CMDC_XACT_WR0 0x1840 ++#define EARCRX_CMDC_XACT_WR1 0x1844 ++#define EARCRX_CMDC_XACT_WR2 0x1848 ++#define EARCRX_CMDC_XACT_WR3 0x184c ++#define EARCRX_CMDC_XACT_WR4 0x1850 ++#define EARCRX_CMDC_XACT_WR5 0x1854 ++#define EARCRX_CMDC_XACT_WR6 0x1858 ++#define EARCRX_CMDC_XACT_WR7 0x185c ++#define EARCRX_CMDC_XACT_WR8 0x1860 ++#define EARCRX_CMDC_XACT_WR9 0x1864 ++#define EARCRX_CMDC_XACT_WR10 0x1868 ++#define EARCRX_CMDC_XACT_WR11 0x186c ++#define EARCRX_CMDC_XACT_WR12 0x1870 ++#define EARCRX_CMDC_XACT_WR13 0x1874 ++#define EARCRX_CMDC_XACT_WR14 0x1878 ++#define EARCRX_CMDC_XACT_WR15 0x187c ++#define EARCRX_CMDC_XACT_WR16 0x1880 ++#define EARCRX_CMDC_XACT_WR17 0x1884 ++#define EARCRX_CMDC_XACT_WR18 0x1888 ++#define EARCRX_CMDC_XACT_WR19 0x188c ++#define EARCRX_CMDC_XACT_WR20 0x1890 ++#define EARCRX_CMDC_XACT_WR21 0x1894 ++#define EARCRX_CMDC_XACT_WR22 0x1898 ++#define EARCRX_CMDC_XACT_WR23 0x189c ++#define EARCRX_CMDC_XACT_WR24 0x18a0 ++#define EARCRX_CMDC_XACT_WR25 0x18a4 ++#define EARCRX_CMDC_XACT_WR26 0x18a8 ++#define EARCRX_CMDC_XACT_WR27 0x18ac ++#define EARCRX_CMDC_XACT_WR28 0x18b0 ++#define EARCRX_CMDC_XACT_WR29 0x18b4 ++#define EARCRX_CMDC_XACT_WR30 0x18b8 ++#define EARCRX_CMDC_XACT_WR31 0x18bc ++#define EARCRX_CMDC_XACT_WR32 0x18c0 ++#define EARCRX_CMDC_XACT_WR33 0x18c4 ++#define EARCRX_CMDC_XACT_WR34 0x18c8 ++#define EARCRX_CMDC_XACT_WR35 0x18cc ++#define EARCRX_CMDC_XACT_WR36 0x18d0 ++#define EARCRX_CMDC_XACT_WR37 0x18d4 ++#define EARCRX_CMDC_XACT_WR38 0x18d8 ++#define EARCRX_CMDC_XACT_WR39 0x18dc ++#define EARCRX_CMDC_XACT_WR40 0x18e0 ++#define EARCRX_CMDC_XACT_WR41 0x18e4 ++#define EARCRX_CMDC_XACT_WR42 0x18e8 ++#define EARCRX_CMDC_XACT_WR43 0x18ec ++#define EARCRX_CMDC_XACT_WR44 0x18f0 ++#define EARCRX_CMDC_XACT_WR45 0x18f4 ++#define EARCRX_CMDC_XACT_WR46 0x18f8 ++#define EARCRX_CMDC_XACT_WR47 0x18fc ++#define EARCRX_CMDC_XACT_WR48 0x1900 ++#define EARCRX_CMDC_XACT_WR49 0x1904 ++#define EARCRX_CMDC_XACT_WR50 0x1908 ++#define EARCRX_CMDC_XACT_WR51 0x190c ++#define EARCRX_CMDC_XACT_WR52 0x1910 ++#define EARCRX_CMDC_XACT_WR53 0x1914 ++#define EARCRX_CMDC_XACT_WR54 0x1918 ++#define EARCRX_CMDC_XACT_WR55 0x191c ++#define EARCRX_CMDC_XACT_WR56 0x1920 ++#define EARCRX_CMDC_XACT_WR57 0x1924 ++#define EARCRX_CMDC_XACT_WR58 0x1928 ++#define EARCRX_CMDC_XACT_WR59 0x192c ++#define EARCRX_CMDC_XACT_WR60 0x1930 ++#define EARCRX_CMDC_XACT_WR61 0x1934 ++#define EARCRX_CMDC_XACT_WR62 0x1938 ++#define EARCRX_CMDC_XACT_WR63 0x193c ++#define EARCRX_CMDC_XACT_WR64 0x1940 ++#define EARCRX_CMDC_XACT_RD0 0x1960 ++#define EARCRX_CMDC_XACT_RD1 0x1964 ++#define EARCRX_CMDC_XACT_RD2 0x1968 ++#define EARCRX_CMDC_XACT_RD3 0x196c ++#define EARCRX_CMDC_XACT_RD4 0x1970 ++#define EARCRX_CMDC_XACT_RD5 0x1974 ++#define EARCRX_CMDC_XACT_RD6 0x1978 ++#define EARCRX_CMDC_XACT_RD7 0x197c ++#define EARCRX_CMDC_XACT_RD8 0x1980 ++#define EARCRX_CMDC_XACT_RD9 0x1984 ++#define EARCRX_CMDC_XACT_RD10 0x1988 ++#define EARCRX_CMDC_XACT_RD11 0x198c ++#define EARCRX_CMDC_XACT_RD12 0x1990 ++#define EARCRX_CMDC_XACT_RD13 0x1994 ++#define EARCRX_CMDC_XACT_RD14 0x1998 ++#define EARCRX_CMDC_XACT_RD15 0x199c ++#define EARCRX_CMDC_XACT_RD16 0x19a0 ++#define EARCRX_CMDC_XACT_RD17 0x19a4 ++#define EARCRX_CMDC_XACT_RD18 0x19a8 ++#define EARCRX_CMDC_XACT_RD19 0x19ac ++#define EARCRX_CMDC_XACT_RD20 0x19b0 ++#define EARCRX_CMDC_XACT_RD21 0x19b4 ++#define EARCRX_CMDC_XACT_RD22 0x19b8 ++#define EARCRX_CMDC_XACT_RD23 0x19bc ++#define EARCRX_CMDC_XACT_RD24 0x19c0 ++#define EARCRX_CMDC_XACT_RD25 0x19c4 ++#define EARCRX_CMDC_XACT_RD26 0x19c8 ++#define EARCRX_CMDC_XACT_RD27 0x19cc ++#define EARCRX_CMDC_XACT_RD28 0x19d0 ++#define EARCRX_CMDC_XACT_RD29 0x19d4 ++#define EARCRX_CMDC_XACT_RD30 0x19d8 ++#define EARCRX_CMDC_XACT_RD31 0x19dc ++#define EARCRX_CMDC_XACT_RD32 0x19e0 ++#define EARCRX_CMDC_XACT_RD33 0x19e4 ++#define EARCRX_CMDC_XACT_RD34 0x19e8 ++#define EARCRX_CMDC_XACT_RD35 0x19ec ++#define EARCRX_CMDC_XACT_RD36 0x19f0 ++#define EARCRX_CMDC_XACT_RD37 0x19f4 ++#define EARCRX_CMDC_XACT_RD38 0x19f8 ++#define EARCRX_CMDC_XACT_RD39 0x19fc ++#define EARCRX_CMDC_XACT_RD40 0x1a00 ++#define EARCRX_CMDC_XACT_RD41 0x1a04 ++#define EARCRX_CMDC_XACT_RD42 0x1a08 ++#define EARCRX_CMDC_XACT_RD43 0x1a0c ++#define EARCRX_CMDC_XACT_RD44 0x1a10 ++#define EARCRX_CMDC_XACT_RD45 0x1a14 ++#define EARCRX_CMDC_XACT_RD46 0x1a18 ++#define EARCRX_CMDC_XACT_RD47 0x1a1c ++#define EARCRX_CMDC_XACT_RD48 0x1a20 ++#define EARCRX_CMDC_XACT_RD49 0x1a24 ++#define EARCRX_CMDC_XACT_RD50 0x1a28 ++#define EARCRX_CMDC_XACT_RD51 0x1a2c ++#define EARCRX_CMDC_XACT_RD52 0x1a30 ++#define EARCRX_CMDC_XACT_RD53 0x1a34 ++#define EARCRX_CMDC_XACT_RD54 0x1a38 ++#define EARCRX_CMDC_XACT_RD55 0x1a3c ++#define EARCRX_CMDC_XACT_RD56 0x1a40 ++#define EARCRX_CMDC_XACT_RD57 0x1a44 ++#define EARCRX_CMDC_XACT_RD58 0x1a48 ++#define EARCRX_CMDC_XACT_RD59 0x1a4c ++#define EARCRX_CMDC_XACT_RD60 0x1a50 ++#define EARCRX_CMDC_XACT_RD61 0x1a54 ++#define EARCRX_CMDC_XACT_RD62 0x1a58 ++#define EARCRX_CMDC_XACT_RD63 0x1a5c ++#define EARCRX_CMDC_XACT_RD64 0x1a60 ++#define EARCRX_CMDC_SYNC_CONFIG 0x1b00 ++/* eARC RX DMAC Registers */ ++#define EARCRX_DMAC_PHY_CONTROL 0x1c00 ++#define EARCRX_DMAC_CONFIG 0x1c08 ++#define EARCRX_DMAC_CONTROL0 0x1c0c ++#define EARCRX_DMAC_AUDIO_EN BIT(1) ++#define EARCRX_DMAC_EN BIT(0) ++#define EARCRX_DMAC_CONTROL1 0x1c10 ++#define EARCRX_DMAC_STATUS 0x1c14 ++#define EARCRX_DMAC_CHSTATUS0 0x1c18 ++#define EARCRX_DMAC_CHSTATUS1 0x1c1c ++#define EARCRX_DMAC_CHSTATUS2 0x1c20 ++#define EARCRX_DMAC_CHSTATUS3 0x1c24 ++#define EARCRX_DMAC_CHSTATUS4 0x1c28 ++#define EARCRX_DMAC_CHSTATUS5 0x1c2c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC0 0x1c30 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC1 0x1c34 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC2 0x1c38 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC3 0x1c3c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC4 0x1c40 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC5 0x1c44 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC6 0x1c48 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC7 0x1c4c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC8 0x1c50 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC9 0x1c54 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC10 0x1c58 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC11 0x1c5c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT0 0x1c60 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT1 0x1c64 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT2 0x1c68 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT3 0x1c6c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT4 0x1c70 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT5 0x1c74 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT6 0x1c78 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT7 0x1c7c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT8 0x1c80 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT9 0x1c84 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT10 0x1c88 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT11 0x1c8c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT0 0x1c90 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT1 0x1c94 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT2 0x1c98 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT3 0x1c9c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT4 0x1ca0 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT5 0x1ca4 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT6 0x1ca8 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT7 0x1cac ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT8 0x1cb0 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT9 0x1cb4 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT10 0x1cb8 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT11 0x1cbc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC0 0x1cc0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC1 0x1cc4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC2 0x1cc8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC3 0x1ccc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC4 0x1cd0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC5 0x1cd4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC6 0x1cd8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC7 0x1cdc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC8 0x1ce0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC9 0x1ce4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC10 0x1ce8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC11 0x1cec ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC12 0x1cf0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC13 0x1cf4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC14 0x1cf8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC15 0x1cfc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC16 0x1d00 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC17 0x1d04 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC18 0x1d08 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC19 0x1d0c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC20 0x1d10 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC21 0x1d14 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC22 0x1d18 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC23 0x1d1c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC24 0x1d20 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC25 0x1d24 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC26 0x1d28 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC27 0x1d2c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC28 0x1d30 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC29 0x1d34 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC30 0x1d38 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC31 0x1d3c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC32 0x1d40 ++#define EARCRX_DMAC_CHSTATUS_STREAMER0 0x1d44 ++#define EARCRX_DMAC_CHSTATUS_STREAMER1 0x1d48 ++#define EARCRX_DMAC_CHSTATUS_STREAMER2 0x1d4c ++#define EARCRX_DMAC_CHSTATUS_STREAMER3 0x1d50 ++#define EARCRX_DMAC_CHSTATUS_STREAMER4 0x1d54 ++#define EARCRX_DMAC_CHSTATUS_STREAMER5 0x1d58 ++#define EARCRX_DMAC_CHSTATUS_STREAMER6 0x1d5c ++#define EARCRX_DMAC_CHSTATUS_STREAMER7 0x1d60 ++#define EARCRX_DMAC_CHSTATUS_STREAMER8 0x1d64 ++#define EARCRX_DMAC_CHSTATUS_STREAMER9 0x1d68 ++#define EARCRX_DMAC_CHSTATUS_STREAMER10 0x1d6c ++#define EARCRX_DMAC_CHSTATUS_STREAMER11 0x1d70 ++#define EARCRX_DMAC_CHSTATUS_STREAMER12 0x1d74 ++#define EARCRX_DMAC_CHSTATUS_STREAMER13 0x1d78 ++#define EARCRX_DMAC_CHSTATUS_STREAMER14 0x1d7c ++#define EARCRX_DMAC_USRDATA_STREAMER0 0x1d80 ++/* Main Unit Interrupt Registers */ ++#define MAIN_INTVEC_INDEX 0x3000 ++#define MAINUNIT_0_INT_STATUS 0x3010 ++#define MAINUNIT_0_INT_MASK_N 0x3014 ++#define MAINUNIT_0_INT_CLEAR 0x3018 ++#define MAINUNIT_0_INT_FORCE 0x301c ++#define MAINUNIT_1_INT_STATUS 0x3020 ++#define FLT_EXIT_TO_LTSL_IRQ BIT(22) ++#define FLT_EXIT_TO_LTS4_IRQ BIT(21) ++#define FLT_EXIT_TO_LTSP_IRQ BIT(20) ++#define SCDC_NACK_RCVD_IRQ BIT(12) ++#define SCDC_RR_REPLY_STOP_IRQ BIT(11) ++#define SCDC_UPD_FLAGS_CLR_IRQ BIT(10) ++#define SCDC_UPD_FLAGS_CHG_IRQ BIT(9) ++#define SCDC_UPD_FLAGS_RD_IRQ BIT(8) ++#define I2CM_NACK_RCVD_IRQ BIT(2) ++#define I2CM_READ_REQUEST_IRQ BIT(1) ++#define I2CM_OP_DONE_IRQ BIT(0) ++#define MAINUNIT_1_INT_MASK_N 0x3024 ++#define I2CM_NACK_RCVD_MASK_N BIT(2) ++#define I2CM_READ_REQUEST_MASK_N BIT(1) ++#define I2CM_OP_DONE_MASK_N BIT(0) ++#define MAINUNIT_1_INT_CLEAR 0x3028 ++#define I2CM_NACK_RCVD_CLEAR BIT(2) ++#define I2CM_READ_REQUEST_CLEAR BIT(1) ++#define I2CM_OP_DONE_CLEAR BIT(0) ++#define MAINUNIT_1_INT_FORCE 0x302c ++/* AVPUNIT Interrupt Registers */ ++#define AVP_INTVEC_INDEX 0x3800 ++#define AVP_0_INT_STATUS 0x3810 ++#define AVP_0_INT_MASK_N 0x3814 ++#define AVP_0_INT_CLEAR 0x3818 ++#define AVP_0_INT_FORCE 0x381c ++#define AVP_1_INT_STATUS 0x3820 ++#define AVP_1_INT_MASK_N 0x3824 ++#define HDCP14_AUTH_CHG_MASK_N BIT(6) ++#define AVP_1_INT_CLEAR 0x3828 ++#define AVP_1_INT_FORCE 0x382c ++#define AVP_2_INT_STATUS 0x3830 ++#define AVP_2_INT_MASK_N 0x3834 ++#define AVP_2_INT_CLEAR 0x3838 ++#define AVP_2_INT_FORCE 0x383c ++#define AVP_3_INT_STATUS 0x3840 ++#define AVP_3_INT_MASK_N 0x3844 ++#define AVP_3_INT_CLEAR 0x3848 ++#define AVP_3_INT_FORCE 0x384c ++#define AVP_4_INT_STATUS 0x3850 ++#define AVP_4_INT_MASK_N 0x3854 ++#define AVP_4_INT_CLEAR 0x3858 ++#define AVP_4_INT_FORCE 0x385c ++#define AVP_5_INT_STATUS 0x3860 ++#define AVP_5_INT_MASK_N 0x3864 ++#define AVP_5_INT_CLEAR 0x3868 ++#define AVP_5_INT_FORCE 0x386c ++#define AVP_6_INT_STATUS 0x3870 ++#define AVP_6_INT_MASK_N 0x3874 ++#define AVP_6_INT_CLEAR 0x3878 ++#define AVP_6_INT_FORCE 0x387c ++/* CEC Interrupt Registers */ ++#define CEC_INT_STATUS 0x4000 ++#define CEC_INT_MASK_N 0x4004 ++#define CEC_INT_CLEAR 0x4008 ++#define CEC_INT_FORCE 0x400c ++/* eARC RX Interrupt Registers */ ++#define EARCRX_INTVEC_INDEX 0x4800 ++#define EARCRX_0_INT_STATUS 0x4810 ++#define EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ BIT(9) ++#define EARCRX_CMDC_DISCOVERY_DONE_IRQ BIT(8) ++#define EARCRX_0_INT_MASK_N 0x4814 ++#define EARCRX_0_INT_CLEAR 0x4818 ++#define EARCRX_0_INT_FORCE 0x481c ++#define EARCRX_1_INT_STATUS 0x4820 ++#define EARCRX_1_INT_MASK_N 0x4824 ++#define EARCRX_1_INT_CLEAR 0x4828 ++#define EARCRX_1_INT_FORCE 0x482c ++ ++#endif /* __DW_HDMI_QP_H__ */ +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index aca5bb0866f8..b2338e567290 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -162,6 +162,8 @@ struct dw_hdmi { + void __iomem *regs; + bool sink_is_hdmi; + bool sink_has_audio; ++ bool support_hdmi; ++ int force_output; + + struct pinctrl *pinctrl; + struct pinctrl_state *default_state; +@@ -254,6 +256,25 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, + hdmi_modb(hdmi, data << shift, mask, reg); + } + ++static bool dw_hdmi_check_output_type_changed(struct dw_hdmi *hdmi) ++{ ++ bool sink_hdmi; ++ ++ sink_hdmi = hdmi->sink_is_hdmi; ++ ++ if (hdmi->force_output == 1) ++ hdmi->sink_is_hdmi = true; ++ else if (hdmi->force_output == 2) ++ hdmi->sink_is_hdmi = false; ++ else ++ hdmi->sink_is_hdmi = hdmi->support_hdmi; ++ ++ if (sink_hdmi != hdmi->sink_is_hdmi) ++ return true; ++ ++ return false; ++} ++ + static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) + { + hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, +@@ -2531,6 +2552,45 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, + return 0; + } + ++void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi) ++{ ++ if (!hdmi->bridge_is_on) ++ return; ++ ++ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); ++ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); ++ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_set_quant_range); ++ ++void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val) ++{ ++ hdmi->force_output = val; ++ ++ if (!dw_hdmi_check_output_type_changed(hdmi)) ++ return; ++ ++ if (!hdmi->bridge_is_on) ++ return; ++ ++ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); ++ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); ++ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_set_output_type); ++ ++bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi) ++{ ++ return hdmi->sink_is_hdmi; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_get_output_whether_hdmi); ++ ++int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi) ++{ ++ return hdmi->support_hdmi; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_get_output_type_cap); ++ + static void dw_hdmi_connector_force(struct drm_connector *connector) + { + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, +@@ -3682,6 +3742,35 @@ void dw_hdmi_unbind(struct dw_hdmi *hdmi) + } + EXPORT_SYMBOL_GPL(dw_hdmi_unbind); + ++void dw_hdmi_suspend(struct dw_hdmi *hdmi) ++{ ++ if (!hdmi) ++ return; ++ ++ mutex_lock(&hdmi->mutex); ++ ++ /* ++ * When system shutdown, hdmi should be disabled. ++ * When system suspend, dw_hdmi_bridge_disable will disable hdmi first. ++ * To prevent duplicate operation, we should determine whether hdmi ++ * has been disabled. ++ */ ++ if (!hdmi->disabled) { ++ hdmi->disabled = true; ++ dw_hdmi_update_power(hdmi); ++ dw_hdmi_update_phy_mask(hdmi); ++ } ++ mutex_unlock(&hdmi->mutex); ++ ++ //[CC: needed?] ++ // if (hdmi->irq) ++ // disable_irq(hdmi->irq); ++ // cancel_delayed_work(&hdmi->work); ++ // flush_workqueue(hdmi->workqueue); ++ pinctrl_pm_select_sleep_state(hdmi->dev); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_suspend); ++ + void dw_hdmi_resume(struct dw_hdmi *hdmi) + { + dw_hdmi_init_hw(hdmi); +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +index af43a0414b78..8ebdec7254f2 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +@@ -851,6 +851,10 @@ enum { + HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00, + HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04, + ++/* HDMI_FC_GCP */ ++ HDMI_FC_GCP_SET_AVMUTE = 0x2, ++ HDMI_FC_GCP_CLEAR_AVMUTE = 0x1, ++ + /* FC_DBGFORCE field values */ + HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10, + HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, +diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +index fe33092abbe7..5c056d9c8e1a 100644 +--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +@@ -4,21 +4,32 @@ + */ + + #include ++#include ++#include + #include + #include + #include + #include ++#include + #include + #include + ++#include ++#include ++#include ++#include + #include + #include + #include + #include + #include + ++#include ++ + #include "rockchip_drm_drv.h" + ++#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) ++ + #define RK3228_GRF_SOC_CON2 0x0408 + #define RK3228_HDMI_SDAIN_MSK BIT(14) + #define RK3228_HDMI_SCLIN_MSK BIT(13) +@@ -29,8 +40,11 @@ + + #define RK3288_GRF_SOC_CON6 0x025C + #define RK3288_HDMI_LCDC_SEL BIT(4) +-#define RK3328_GRF_SOC_CON2 0x0408 ++#define RK3288_GRF_SOC_CON16 0x03a8 ++#define RK3288_HDMI_LCDC0_YUV420 BIT(2) ++#define RK3288_HDMI_LCDC1_YUV420 BIT(3) + ++#define RK3328_GRF_SOC_CON2 0x0408 + #define RK3328_HDMI_SDAIN_MSK BIT(11) + #define RK3328_HDMI_SCLIN_MSK BIT(10) + #define RK3328_HDMI_HPD_IOE BIT(2) +@@ -54,32 +68,177 @@ + #define RK3568_HDMI_SDAIN_MSK BIT(15) + #define RK3568_HDMI_SCLIN_MSK BIT(14) + +-#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) ++#define RK3588_GRF_SOC_CON2 0x0308 ++#define RK3588_HDMI1_HPD_INT_MSK BIT(15) ++#define RK3588_HDMI1_HPD_INT_CLR BIT(14) ++#define RK3588_HDMI0_HPD_INT_MSK BIT(13) ++#define RK3588_HDMI0_HPD_INT_CLR BIT(12) ++#define RK3588_GRF_SOC_CON7 0x031c ++#define RK3588_SET_HPD_PATH_MASK (0x3 << 12) ++#define RK3588_GRF_SOC_STATUS1 0x0384 ++#define RK3588_HDMI0_LOW_MORETHAN100MS BIT(20) ++#define RK3588_HDMI0_HPD_PORT_LEVEL BIT(19) ++#define RK3588_HDMI0_IHPD_PORT BIT(18) ++#define RK3588_HDMI0_OHPD_INT BIT(17) ++#define RK3588_HDMI0_LEVEL_INT BIT(16) ++#define RK3588_HDMI0_INTR_CHANGE_CNT (0x7 << 13) ++#define RK3588_HDMI1_LOW_MORETHAN100MS BIT(28) ++#define RK3588_HDMI1_HPD_PORT_LEVEL BIT(27) ++#define RK3588_HDMI1_IHPD_PORT BIT(26) ++#define RK3588_HDMI1_OHPD_INT BIT(25) ++#define RK3588_HDMI1_LEVEL_INT BIT(24) ++#define RK3588_HDMI1_INTR_CHANGE_CNT (0x7 << 21) ++ ++#define RK3588_GRF_VO1_CON3 0x000c ++#define RK3588_COLOR_FORMAT_MASK 0xf ++#define RK3588_YUV444 0x2 ++#define RK3588_YUV420 0x3 ++#define RK3588_COMPRESSED_DATA 0xb ++#define RK3588_COLOR_DEPTH_MASK (0xf << 4) ++#define RK3588_8BPC (0x5 << 4) ++#define RK3588_10BPC (0x6 << 4) ++#define RK3588_CECIN_MASK BIT(8) ++#define RK3588_SCLIN_MASK BIT(9) ++#define RK3588_SDAIN_MASK BIT(10) ++#define RK3588_MODE_MASK BIT(11) ++#define RK3588_COMPRESS_MODE_MASK BIT(12) ++#define RK3588_I2S_SEL_MASK BIT(13) ++#define RK3588_SPDIF_SEL_MASK BIT(14) ++#define RK3588_GRF_VO1_CON4 0x0010 ++#define RK3588_HDMI21_MASK BIT(0) ++#define RK3588_GRF_VO1_CON9 0x0024 ++#define RK3588_HDMI0_GRANT_SEL BIT(10) ++#define RK3588_HDMI0_GRANT_SW BIT(11) ++#define RK3588_HDMI1_GRANT_SEL BIT(12) ++#define RK3588_HDMI1_GRANT_SW BIT(13) ++#define RK3588_GRF_VO1_CON6 0x0018 ++#define RK3588_GRF_VO1_CON7 0x001c ++ ++#define COLOR_DEPTH_10BIT BIT(31) ++#define HDMI_FRL_MODE BIT(30) ++#define HDMI_EARC_MODE BIT(29) ++ ++#define HDMI20_MAX_RATE 600000 ++#define HDMI_8K60_RATE 2376000 + + /** + * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips + * @lcdsel_grf_reg: grf register offset of lcdc select ++ * @ddc_en_reg: grf register offset of hdmi ddc enable + * @lcdsel_big: reg value of selecting vop big for HDMI + * @lcdsel_lit: reg value of selecting vop little for HDMI ++ * @split_mode: flag indicating split mode capability + */ + struct rockchip_hdmi_chip_data { + int lcdsel_grf_reg; ++ int ddc_en_reg; + u32 lcdsel_big; + u32 lcdsel_lit; ++ bool split_mode; ++}; ++ ++enum hdmi_frl_rate_per_lane { ++ FRL_12G_PER_LANE = 12, ++ FRL_10G_PER_LANE = 10, ++ FRL_8G_PER_LANE = 8, ++ FRL_6G_PER_LANE = 6, ++ FRL_3G_PER_LANE = 3, ++}; ++ ++enum rk_if_color_depth { ++ RK_IF_DEPTH_8, ++ RK_IF_DEPTH_10, ++ RK_IF_DEPTH_12, ++ RK_IF_DEPTH_16, ++ RK_IF_DEPTH_420_10, ++ RK_IF_DEPTH_420_12, ++ RK_IF_DEPTH_420_16, ++ RK_IF_DEPTH_6, ++ RK_IF_DEPTH_MAX, ++}; ++ ++enum rk_if_color_format { ++ RK_IF_FORMAT_RGB, /* default RGB */ ++ RK_IF_FORMAT_YCBCR444, /* YCBCR 444 */ ++ RK_IF_FORMAT_YCBCR422, /* YCBCR 422 */ ++ RK_IF_FORMAT_YCBCR420, /* YCBCR 420 */ ++ RK_IF_FORMAT_YCBCR_HQ, /* Highest subsampled YUV */ ++ RK_IF_FORMAT_YCBCR_LQ, /* Lowest subsampled YUV */ ++ RK_IF_FORMAT_MAX, + }; + + struct rockchip_hdmi { + struct device *dev; + struct regmap *regmap; ++ struct regmap *vo1_regmap; + struct rockchip_encoder encoder; ++ struct drm_device *drm_dev; + const struct rockchip_hdmi_chip_data *chip_data; +- const struct dw_hdmi_plat_data *plat_data; ++ struct dw_hdmi_plat_data *plat_data; ++ struct clk *aud_clk; + struct clk *ref_clk; + struct clk *grf_clk; ++ struct clk *hclk_vio; ++ struct clk *hclk_vo1; ++ struct clk *hclk_vop; ++ struct clk *hpd_clk; ++ struct clk *pclk; ++ struct clk *earc_clk; ++ struct clk *hdmitx_ref; + struct dw_hdmi *hdmi; ++ struct dw_hdmi_qp *hdmi_qp; ++ + struct regulator *avdd_0v9; + struct regulator *avdd_1v8; + struct phy *phy; ++ ++ u32 max_tmdsclk; ++ bool unsupported_yuv_input; ++ bool unsupported_deep_color; ++ bool skip_check_420_mode; ++ u8 force_output; ++ u8 id; ++ bool hpd_stat; ++ bool is_hdmi_qp; ++ bool user_split_mode; ++ ++ unsigned long bus_format; ++ unsigned long output_bus_format; ++ unsigned long enc_out_encoding; ++ int color_changed; ++ int hpd_irq; ++ int vp_id; ++ ++ struct drm_property *color_depth_property; ++ struct drm_property *hdmi_output_property; ++ struct drm_property *colordepth_capacity; ++ struct drm_property *outputmode_capacity; ++ struct drm_property *quant_range; ++ struct drm_property *hdr_panel_metadata_property; ++ struct drm_property *next_hdr_sink_data_property; ++ struct drm_property *output_hdmi_dvi; ++ struct drm_property *output_type_capacity; ++ struct drm_property *user_split_mode_prop; ++ ++ struct drm_property_blob *hdr_panel_blob_ptr; ++ struct drm_property_blob *next_hdr_data_ptr; ++ ++ unsigned int colordepth; ++ unsigned int colorimetry; ++ unsigned int hdmi_quant_range; ++ unsigned int phy_bus_width; ++ enum rk_if_color_format hdmi_output; ++ // struct rockchip_drm_sub_dev sub_dev; ++ ++ u8 max_frl_rate_per_lane; ++ u8 max_lanes; ++ // struct rockchip_drm_dsc_cap dsc_cap; ++ // struct next_hdr_sink_data next_hdr_data; ++ struct dw_hdmi_link_config link_cfg; ++ struct gpio_desc *enable_gpio; ++ ++ struct delayed_work work; ++ struct workqueue_struct *workqueue; + }; + + static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder) +@@ -202,13 +361,831 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = { + /*pixelclk symbol term vlev*/ + { 74250000, 0x8009, 0x0004, 0x0272}, + { 148500000, 0x802b, 0x0004, 0x028d}, ++ { 165000000, 0x802b, 0x0004, 0x0209}, + { 297000000, 0x8039, 0x0005, 0x028d}, ++ { 594000000, 0x8039, 0x0000, 0x019d}, + { ~0UL, 0x0000, 0x0000, 0x0000} + }; + ++enum ROW_INDEX_BPP { ++ ROW_INDEX_6BPP = 0, ++ ROW_INDEX_8BPP, ++ ROW_INDEX_10BPP, ++ ROW_INDEX_12BPP, ++ ROW_INDEX_23BPP, ++ MAX_ROW_INDEX ++}; ++ ++enum COLUMN_INDEX_BPC { ++ COLUMN_INDEX_8BPC = 0, ++ COLUMN_INDEX_10BPC, ++ COLUMN_INDEX_12BPC, ++ COLUMN_INDEX_14BPC, ++ COLUMN_INDEX_16BPC, ++ MAX_COLUMN_INDEX ++}; ++ ++#define PPS_TABLE_LEN 8 ++#define PPS_BPP_LEN 4 ++#define PPS_BPC_LEN 2 ++ ++struct pps_data { ++ u32 pic_width; ++ u32 pic_height; ++ u32 slice_width; ++ u32 slice_height; ++ bool convert_rgb; ++ u8 bpc; ++ u8 bpp; ++ u8 raw_pps[128]; ++}; ++ ++#if 0 ++/* ++ * Selected Rate Control Related Parameter Recommended Values ++ * from DSC_v1.11 spec & C Model release: DSC_model_20161212 ++ */ ++static struct pps_data pps_datas[PPS_TABLE_LEN] = { ++ { ++ /* 7680x4320/960X96 rgb 8bpc 12bpp */ ++ 7680, 4320, 960, 96, 1, 8, 192, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xc0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0, ++ 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9, ++ 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa, ++ 0x08, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0x82, 0x00, 0xc0, 0x09, 0x00, ++ 0x09, 0x7e, 0x19, 0xbc, 0x19, 0xba, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76, ++ 0x2a, 0x76, 0x2a, 0x74, 0x3a, 0xb4, 0x52, 0xf4, ++ 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 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 8bpc 11bpp */ ++ 7680, 4320, 960, 96, 1, 8, 176, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xb0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28, ++ 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0, ++ 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33, ++ 0x0f, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0x82, 0x01, 0x00, 0x09, 0x40, ++ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76, ++ 0x2a, 0x76, 0x2a, 0xb4, 0x3a, 0xb4, 0x52, 0xf4, ++ 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 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 8bpc 10bpp */ ++ 7680, 4320, 960, 96, 1, 8, 160, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xa0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0, ++ 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0, ++ 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb, ++ 0x16, 0x00, 0x10, 0xec, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40, ++ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6, ++ 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x5b, 0x34, ++ 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 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 8bpc 9bpp */ ++ 7680, 4320, 960, 96, 1, 8, 144, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0x90, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38, ++ 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7, ++ 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa, ++ 0x17, 0x00, 0x10, 0xf1, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40, ++ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6, ++ 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x63, 0x74, ++ 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 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 12bpp */ ++ 7680, 4320, 960, 96, 1, 10, 192, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0xc0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0, ++ 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9, ++ 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa, ++ 0x08, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0x02, 0x11, 0x80, 0x22, 0x00, ++ 0x22, 0x7e, 0x32, 0xbc, 0x32, 0xba, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76, ++ 0x4b, 0x76, 0x4b, 0x74, 0x5b, 0xb4, 0x73, 0xf4, ++ 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 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 11bpp */ ++ 7680, 4320, 960, 96, 1, 10, 176, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0xb0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28, ++ 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0, ++ 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33, ++ 0x0f, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0x42, 0x19, 0xc0, 0x2a, 0x40, ++ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76, ++ 0x4b, 0x76, 0x4b, 0xb4, 0x5b, 0xb4, 0x73, 0xf4, ++ 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 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 10bpp */ ++ 7680, 4320, 960, 96, 1, 10, 160, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0xa0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0, ++ 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0, ++ 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb, ++ 0x16, 0x00, 0x10, 0xec, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40, ++ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6, ++ 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x7c, 0x34, ++ 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 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 9bpp */ ++ 7680, 4320, 960, 96, 1, 10, 144, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0x90, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38, ++ 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7, ++ 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa, ++ 0x17, 0x00, 0x10, 0xf1, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40, ++ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6, ++ 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x84, 0x74, ++ 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 ++ }, ++ }, ++}; ++ ++static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++#endif ++ ++static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static int hdmi_bus_fmt_color_depth(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ return 8; ++ ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ return 10; ++ ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ return 12; ++ ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return 16; ++ ++ default: ++ return 0; ++ } ++} ++ ++static unsigned int ++hdmi_get_tmdsclock(struct rockchip_hdmi *hdmi, unsigned long pixelclock) ++{ ++ unsigned int tmdsclock = pixelclock; ++ unsigned int depth = ++ hdmi_bus_fmt_color_depth(hdmi->output_bus_format); ++ ++ if (!hdmi_bus_fmt_is_yuv422(hdmi->output_bus_format)) { ++ switch (depth) { ++ case 16: ++ tmdsclock = pixelclock * 2; ++ break; ++ case 12: ++ tmdsclock = pixelclock * 3 / 2; ++ break; ++ case 10: ++ tmdsclock = pixelclock * 5 / 4; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return tmdsclock; ++} ++ ++static int rockchip_hdmi_match_by_id(struct device *dev, const void *data) ++{ ++ struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); ++ const unsigned int *id = data; ++ ++ return hdmi->id == *id; ++} ++ ++static struct rockchip_hdmi * ++rockchip_hdmi_find_by_id(struct device_driver *drv, unsigned int id) ++{ ++ struct device *dev; ++ ++ dev = driver_find_device(drv, NULL, &id, rockchip_hdmi_match_by_id); ++ if (!dev) ++ return NULL; ++ ++ return dev_get_drvdata(dev); ++} ++ ++static void hdmi_select_link_config(struct rockchip_hdmi *hdmi, ++ struct drm_crtc_state *crtc_state, ++ unsigned int tmdsclk) ++{ ++ struct drm_display_mode mode; ++ int max_lanes, max_rate_per_lane; ++ // int max_dsc_lanes, max_dsc_rate_per_lane; ++ unsigned long max_frl_rate; ++ ++ drm_mode_copy(&mode, &crtc_state->mode); ++ ++ max_lanes = hdmi->max_lanes; ++ max_rate_per_lane = hdmi->max_frl_rate_per_lane; ++ max_frl_rate = max_lanes * max_rate_per_lane * 1000000; ++ ++ hdmi->link_cfg.dsc_mode = false; ++ hdmi->link_cfg.frl_lanes = max_lanes; ++ hdmi->link_cfg.rate_per_lane = max_rate_per_lane; ++ ++ if (!max_frl_rate || (tmdsclk < HDMI20_MAX_RATE && mode.clock < HDMI20_MAX_RATE)) { ++ dev_info(hdmi->dev, "use tmds hdmi mode\n"); ++ hdmi->link_cfg.frl_mode = false; ++ return; ++ } ++ ++ hdmi->link_cfg.frl_mode = true; ++ dev_info(hdmi->dev, "use frl hdmi mode\n"); ++ ++ // if (!hdmi->dsc_cap.v_1p2) ++ // return; ++ // ++ // max_dsc_lanes = hdmi->dsc_cap.max_lanes; ++ // max_dsc_rate_per_lane = ++ // hdmi->dsc_cap.max_frl_rate_per_lane; ++ // ++ // if (mode.clock >= HDMI_8K60_RATE && ++ // !hdmi_bus_fmt_is_yuv420(hdmi->bus_format) && ++ // !hdmi_bus_fmt_is_yuv422(hdmi->bus_format)) { ++ // hdmi->link_cfg.dsc_mode = true; ++ // hdmi->link_cfg.frl_lanes = max_dsc_lanes; ++ // hdmi->link_cfg.rate_per_lane = max_dsc_rate_per_lane; ++ // } else { ++ // hdmi->link_cfg.dsc_mode = false; ++ // hdmi->link_cfg.frl_lanes = max_lanes; ++ // hdmi->link_cfg.rate_per_lane = max_rate_per_lane; ++ // } ++} ++ ++///////////////////////////////////////////////////////////////////////////////////// ++/* CC: disable DSC */ ++#if 0 ++static int hdmi_dsc_get_slice_height(int vactive) ++{ ++ int slice_height; ++ ++ /* ++ * Slice Height determination : HDMI2.1 Section 7.7.5.2 ++ * Select smallest slice height >=96, that results in a valid PPS and ++ * requires minimum padding lines required for final slice. ++ * ++ * Assumption : Vactive is even. ++ */ ++ for (slice_height = 96; slice_height <= vactive; slice_height += 2) ++ if (vactive % slice_height == 0) ++ return slice_height; ++ ++ return 0; ++} ++ ++static int hdmi_dsc_get_num_slices(struct rockchip_hdmi *hdmi, ++ struct drm_crtc_state *crtc_state, ++ int src_max_slices, int src_max_slice_width, ++ int hdmi_max_slices, int hdmi_throughput) ++{ ++/* Pixel rates in KPixels/sec */ ++#define HDMI_DSC_PEAK_PIXEL_RATE 2720000 ++/* ++ * Rates at which the source and sink are required to process pixels in each ++ * slice, can be two levels: either at least 340000KHz or at least 40000KHz. ++ */ ++#define HDMI_DSC_MAX_ENC_THROUGHPUT_0 340000 ++#define HDMI_DSC_MAX_ENC_THROUGHPUT_1 400000 ++ ++/* Spec limits the slice width to 2720 pixels */ ++#define MAX_HDMI_SLICE_WIDTH 2720 ++ int kslice_adjust; ++ int adjusted_clk_khz; ++ int min_slices; ++ int target_slices; ++ int max_throughput; /* max clock freq. in khz per slice */ ++ int max_slice_width; ++ int slice_width; ++ int pixel_clock = crtc_state->mode.clock; ++ ++ if (!hdmi_throughput) ++ return 0; ++ ++ /* ++ * Slice Width determination : HDMI2.1 Section 7.7.5.1 ++ * kslice_adjust factor for 4:2:0, and 4:2:2 formats is 0.5, where as ++ * for 4:4:4 is 1.0. Multiplying these factors by 10 and later ++ * dividing adjusted clock value by 10. ++ */ ++ if (hdmi_bus_fmt_is_yuv444(hdmi->output_bus_format) || ++ hdmi_bus_fmt_is_rgb(hdmi->output_bus_format)) ++ kslice_adjust = 10; ++ else ++ kslice_adjust = 5; ++ ++ /* ++ * As per spec, the rate at which the source and the sink process ++ * the pixels per slice are at two levels: at least 340Mhz or 400Mhz. ++ * This depends upon the pixel clock rate and output formats ++ * (kslice adjust). ++ * If pixel clock * kslice adjust >= 2720MHz slices can be processed ++ * at max 340MHz, otherwise they can be processed at max 400MHz. ++ */ ++ ++ adjusted_clk_khz = DIV_ROUND_UP(kslice_adjust * pixel_clock, 10); ++ ++ if (adjusted_clk_khz <= HDMI_DSC_PEAK_PIXEL_RATE) ++ max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_0; ++ else ++ max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_1; ++ ++ /* ++ * Taking into account the sink's capability for maximum ++ * clock per slice (in MHz) as read from HF-VSDB. ++ */ ++ max_throughput = min(max_throughput, hdmi_throughput * 1000); ++ ++ min_slices = DIV_ROUND_UP(adjusted_clk_khz, max_throughput); ++ max_slice_width = min(MAX_HDMI_SLICE_WIDTH, src_max_slice_width); ++ ++ /* ++ * Keep on increasing the num of slices/line, starting from min_slices ++ * per line till we get such a number, for which the slice_width is ++ * just less than max_slice_width. The slices/line selected should be ++ * less than or equal to the max horizontal slices that the combination ++ * of PCON encoder and HDMI decoder can support. ++ */ ++ do { ++ if (min_slices <= 1 && src_max_slices >= 1 && hdmi_max_slices >= 1) ++ target_slices = 1; ++ else if (min_slices <= 2 && src_max_slices >= 2 && hdmi_max_slices >= 2) ++ target_slices = 2; ++ else if (min_slices <= 4 && src_max_slices >= 4 && hdmi_max_slices >= 4) ++ target_slices = 4; ++ else if (min_slices <= 8 && src_max_slices >= 8 && hdmi_max_slices >= 8) ++ target_slices = 8; ++ else if (min_slices <= 12 && src_max_slices >= 12 && hdmi_max_slices >= 12) ++ target_slices = 12; ++ else if (min_slices <= 16 && src_max_slices >= 16 && hdmi_max_slices >= 16) ++ target_slices = 16; ++ else ++ return 0; ++ ++ slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, target_slices); ++ if (slice_width > max_slice_width) ++ min_slices = target_slices + 1; ++ } while (slice_width > max_slice_width); ++ ++ return target_slices; ++} ++ ++static int hdmi_dsc_slices(struct rockchip_hdmi *hdmi, ++ struct drm_crtc_state *crtc_state) ++{ ++ int hdmi_throughput = hdmi->dsc_cap.clk_per_slice; ++ int hdmi_max_slices = hdmi->dsc_cap.max_slices; ++ int rk_max_slices = 8; ++ int rk_max_slice_width = 2048; ++ ++ return hdmi_dsc_get_num_slices(hdmi, crtc_state, rk_max_slices, ++ rk_max_slice_width, ++ hdmi_max_slices, hdmi_throughput); ++} ++ ++static int ++hdmi_dsc_get_bpp(struct rockchip_hdmi *hdmi, int src_fractional_bpp, ++ int slice_width, int num_slices, bool hdmi_all_bpp, ++ int hdmi_max_chunk_bytes) ++{ ++ int max_dsc_bpp, min_dsc_bpp; ++ int target_bytes; ++ bool bpp_found = false; ++ int bpp_decrement_x16; ++ int bpp_target; ++ int bpp_target_x16; ++ ++ /* ++ * Get min bpp and max bpp as per Table 7.23, in HDMI2.1 spec ++ * Start with the max bpp and keep on decrementing with ++ * fractional bpp, if supported by PCON DSC encoder ++ * ++ * for each bpp we check if no of bytes can be supported by HDMI sink ++ */ ++ ++ /* only 9\10\12 bpp was tested */ ++ min_dsc_bpp = 9; ++ max_dsc_bpp = 12; ++ ++ /* ++ * Taking into account if all dsc_all_bpp supported by HDMI2.1 sink ++ * Section 7.7.34 : Source shall not enable compressed Video ++ * Transport with bpp_target settings above 12 bpp unless ++ * DSC_all_bpp is set to 1. ++ */ ++ if (!hdmi_all_bpp) ++ max_dsc_bpp = min(max_dsc_bpp, 12); ++ ++ /* ++ * The Sink has a limit of compressed data in bytes for a scanline, ++ * as described in max_chunk_bytes field in HFVSDB block of edid. ++ * The no. of bytes depend on the target bits per pixel that the ++ * source configures. So we start with the max_bpp and calculate ++ * the target_chunk_bytes. We keep on decrementing the target_bpp, ++ * till we get the target_chunk_bytes just less than what the sink's ++ * max_chunk_bytes, or else till we reach the min_dsc_bpp. ++ * ++ * The decrement is according to the fractional support from PCON DSC ++ * encoder. For fractional BPP we use bpp_target as a multiple of 16. ++ * ++ * bpp_target_x16 = bpp_target * 16 ++ * So we need to decrement by {1, 2, 4, 8, 16} for fractional bpps ++ * {1/16, 1/8, 1/4, 1/2, 1} respectively. ++ */ ++ ++ bpp_target = max_dsc_bpp; ++ ++ /* src does not support fractional bpp implies decrement by 16 for bppx16 */ ++ if (!src_fractional_bpp) ++ src_fractional_bpp = 1; ++ bpp_decrement_x16 = DIV_ROUND_UP(16, src_fractional_bpp); ++ bpp_target_x16 = bpp_target * 16; ++ ++ while (bpp_target_x16 > (min_dsc_bpp * 16)) { ++ int bpp; ++ ++ bpp = DIV_ROUND_UP(bpp_target_x16, 16); ++ target_bytes = DIV_ROUND_UP((num_slices * slice_width * bpp), 8); ++ if (target_bytes <= hdmi_max_chunk_bytes) { ++ bpp_found = true; ++ break; ++ } ++ bpp_target_x16 -= bpp_decrement_x16; ++ } ++ if (bpp_found) ++ return bpp_target_x16; ++ ++ return 0; ++} ++ ++static int ++dw_hdmi_dsc_bpp(struct rockchip_hdmi *hdmi, ++ int num_slices, int slice_width) ++{ ++ bool hdmi_all_bpp = hdmi->dsc_cap.all_bpp; ++ int fractional_bpp = 0; ++ int hdmi_max_chunk_bytes = hdmi->dsc_cap.total_chunk_kbytes * 1024; ++ ++ return hdmi_dsc_get_bpp(hdmi, fractional_bpp, slice_width, ++ num_slices, hdmi_all_bpp, ++ hdmi_max_chunk_bytes); ++} ++ ++static int dw_hdmi_qp_set_link_cfg(struct rockchip_hdmi *hdmi, ++ u16 pic_width, u16 pic_height, ++ u16 slice_width, u16 slice_height, ++ u16 bits_per_pixel, u8 bits_per_component) ++{ ++ int i; ++ ++ for (i = 0; i < PPS_TABLE_LEN; i++) ++ if (pic_width == pps_datas[i].pic_width && ++ pic_height == pps_datas[i].pic_height && ++ slice_width == pps_datas[i].slice_width && ++ slice_height == pps_datas[i].slice_height && ++ bits_per_component == pps_datas[i].bpc && ++ bits_per_pixel == pps_datas[i].bpp && ++ hdmi_bus_fmt_is_rgb(hdmi->output_bus_format) == pps_datas[i].convert_rgb) ++ break; ++ ++ if (i == PPS_TABLE_LEN) { ++ dev_err(hdmi->dev, "can't find pps cfg!\n"); ++ return -EINVAL; ++ } ++ ++ memcpy(hdmi->link_cfg.pps_payload, pps_datas[i].raw_pps, 128); ++ hdmi->link_cfg.hcactive = DIV_ROUND_UP(slice_width * (bits_per_pixel / 16), 8) * ++ (pic_width / slice_width); ++ ++ return 0; ++} ++ ++static void dw_hdmi_qp_dsc_configure(struct rockchip_hdmi *hdmi, ++ struct rockchip_crtc_state *s, ++ struct drm_crtc_state *crtc_state) ++{ ++ int ret; ++ int slice_height; ++ int slice_width; ++ int bits_per_pixel; ++ int slice_count; ++ bool hdmi_is_dsc_1_2; ++ unsigned int depth = hdmi_bus_fmt_color_depth(hdmi->output_bus_format); ++ ++ if (!crtc_state) ++ return; ++ ++ hdmi_is_dsc_1_2 = hdmi->dsc_cap.v_1p2; ++ ++ if (!hdmi_is_dsc_1_2) ++ return; ++ ++ slice_height = hdmi_dsc_get_slice_height(crtc_state->mode.vdisplay); ++ if (!slice_height) ++ return; ++ ++ slice_count = hdmi_dsc_slices(hdmi, crtc_state); ++ if (!slice_count) ++ return; ++ ++ slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, slice_count); ++ ++ bits_per_pixel = dw_hdmi_dsc_bpp(hdmi, slice_count, slice_width); ++ if (!bits_per_pixel) ++ return; ++ ++ ret = dw_hdmi_qp_set_link_cfg(hdmi, crtc_state->mode.hdisplay, ++ crtc_state->mode.vdisplay, slice_width, ++ slice_height, bits_per_pixel, depth); ++ ++ if (ret) { ++ dev_err(hdmi->dev, "set vdsc cfg failed\n"); ++ return; ++ } ++ dev_info(hdmi->dev, "dsc_enable\n"); ++ s->dsc_enable = 1; ++ s->dsc_sink_cap.version_major = 1; ++ s->dsc_sink_cap.version_minor = 2; ++ s->dsc_sink_cap.slice_width = slice_width; ++ s->dsc_sink_cap.slice_height = slice_height; ++ s->dsc_sink_cap.target_bits_per_pixel_x16 = bits_per_pixel; ++ s->dsc_sink_cap.block_pred = 1; ++ s->dsc_sink_cap.native_420 = 0; ++ ++ memcpy(&s->pps, hdmi->link_cfg.pps_payload, 128); ++} ++#endif ++///////////////////////////////////////////////////////////////////////////////////////// ++ ++// static int rockchip_hdmi_update_phy_table(struct rockchip_hdmi *hdmi, ++// u32 *config, ++// int phy_table_size) ++// { ++// int i; ++// ++// if (phy_table_size > ARRAY_SIZE(rockchip_phy_config)) { ++// dev_err(hdmi->dev, "phy table array number is out of range\n"); ++// return -E2BIG; ++// } ++// ++// for (i = 0; i < phy_table_size; i++) { ++// if (config[i * 4] != 0) ++// rockchip_phy_config[i].mpixelclock = (u64)config[i * 4]; ++// else ++// rockchip_phy_config[i].mpixelclock = ~0UL; ++// rockchip_phy_config[i].sym_ctr = (u16)config[i * 4 + 1]; ++// rockchip_phy_config[i].term = (u16)config[i * 4 + 2]; ++// rockchip_phy_config[i].vlev_ctr = (u16)config[i * 4 + 3]; ++// } ++// ++// return 0; ++// } ++ ++static void repo_hpd_event(struct work_struct *p_work) ++{ ++ struct rockchip_hdmi *hdmi = container_of(p_work, struct rockchip_hdmi, work.work); ++ bool change; ++ ++ change = drm_helper_hpd_irq_event(hdmi->drm_dev); ++ if (change) { ++ dev_dbg(hdmi->dev, "hpd stat changed:%d\n", hdmi->hpd_stat); ++ // dw_hdmi_qp_cec_set_hpd(hdmi->hdmi_qp, hdmi->hpd_stat, change); ++ } ++} ++ ++static irqreturn_t rockchip_hdmi_hardirq(int irq, void *dev_id) ++{ ++ struct rockchip_hdmi *hdmi = dev_id; ++ u32 intr_stat, val; ++ ++ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); ++ ++ if (intr_stat) { ++ dev_dbg(hdmi->dev, "hpd irq %#x\n", intr_stat); ++ ++ if (!hdmi->id) ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, ++ RK3588_HDMI0_HPD_INT_MSK); ++ else ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, ++ RK3588_HDMI1_HPD_INT_MSK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t rockchip_hdmi_irq(int irq, void *dev_id) ++{ ++ struct rockchip_hdmi *hdmi = dev_id; ++ u32 intr_stat, val; ++ int msecs; ++ bool stat; ++ ++ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); ++ ++ if (!intr_stat) ++ return IRQ_NONE; ++ ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, ++ RK3588_HDMI0_HPD_INT_CLR); ++ if (intr_stat & RK3588_HDMI0_LEVEL_INT) ++ stat = true; ++ else ++ stat = false; ++ } else { ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, ++ RK3588_HDMI1_HPD_INT_CLR); ++ if (intr_stat & RK3588_HDMI1_LEVEL_INT) ++ stat = true; ++ else ++ stat = false; ++ } ++ ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ ++ if (stat) { ++ hdmi->hpd_stat = true; ++ msecs = 150; ++ } else { ++ hdmi->hpd_stat = false; ++ msecs = 20; ++ } ++ mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs)); ++ ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, ++ RK3588_HDMI0_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); ++ } else { ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, ++ RK3588_HDMI1_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); ++ } ++ ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ ++ return IRQ_HANDLED; ++} ++ + static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + { + struct device_node *np = hdmi->dev->of_node; ++ int ret; + + hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(hdmi->regmap)) { +@@ -216,6 +1193,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + return PTR_ERR(hdmi->regmap); + } + ++ if (hdmi->is_hdmi_qp) { ++ hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf"); ++ if (IS_ERR(hdmi->vo1_regmap)) { ++ DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,vo1_grf\n"); ++ return PTR_ERR(hdmi->vo1_regmap); ++ } ++ } ++ + hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref"); + if (!hdmi->ref_clk) + hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll"); +@@ -245,6 +1230,79 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + if (IS_ERR(hdmi->avdd_1v8)) + return PTR_ERR(hdmi->avdd_1v8); + ++ hdmi->hclk_vio = devm_clk_get(hdmi->dev, "hclk_vio"); ++ if (PTR_ERR(hdmi->hclk_vio) == -ENOENT) { ++ hdmi->hclk_vio = NULL; ++ } else if (PTR_ERR(hdmi->hclk_vio) == -EPROBE_DEFER) { ++ return -EPROBE_DEFER; ++ } else if (IS_ERR(hdmi->hclk_vio)) { ++ dev_err(hdmi->dev, "failed to get hclk_vio clock\n"); ++ return PTR_ERR(hdmi->hclk_vio); ++ } ++ ++ hdmi->hclk_vop = devm_clk_get(hdmi->dev, "hclk"); ++ if (PTR_ERR(hdmi->hclk_vop) == -ENOENT) { ++ hdmi->hclk_vop = NULL; ++ } else if (PTR_ERR(hdmi->hclk_vop) == -EPROBE_DEFER) { ++ return -EPROBE_DEFER; ++ } else if (IS_ERR(hdmi->hclk_vop)) { ++ dev_err(hdmi->dev, "failed to get hclk_vop clock\n"); ++ return PTR_ERR(hdmi->hclk_vop); ++ } ++ ++ hdmi->aud_clk = devm_clk_get_optional(hdmi->dev, "aud"); ++ if (IS_ERR(hdmi->aud_clk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->aud_clk), ++ "failed to get aud_clk clock\n"); ++ return PTR_ERR(hdmi->aud_clk); ++ } ++ ++ hdmi->hpd_clk = devm_clk_get_optional(hdmi->dev, "hpd"); ++ if (IS_ERR(hdmi->hpd_clk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hpd_clk), ++ "failed to get hpd_clk clock\n"); ++ return PTR_ERR(hdmi->hpd_clk); ++ } ++ ++ hdmi->hclk_vo1 = devm_clk_get_optional(hdmi->dev, "hclk_vo1"); ++ if (IS_ERR(hdmi->hclk_vo1)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hclk_vo1), ++ "failed to get hclk_vo1 clock\n"); ++ return PTR_ERR(hdmi->hclk_vo1); ++ } ++ ++ hdmi->earc_clk = devm_clk_get_optional(hdmi->dev, "earc"); ++ if (IS_ERR(hdmi->earc_clk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->earc_clk), ++ "failed to get earc_clk clock\n"); ++ return PTR_ERR(hdmi->earc_clk); ++ } ++ ++ hdmi->hdmitx_ref = devm_clk_get_optional(hdmi->dev, "hdmitx_ref"); ++ if (IS_ERR(hdmi->hdmitx_ref)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmitx_ref), ++ "failed to get hdmitx_ref clock\n"); ++ return PTR_ERR(hdmi->hdmitx_ref); ++ } ++ ++ hdmi->pclk = devm_clk_get_optional(hdmi->dev, "pclk"); ++ if (IS_ERR(hdmi->pclk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->pclk), ++ "failed to get pclk clock\n"); ++ return PTR_ERR(hdmi->pclk); ++ } ++ ++ hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(hdmi->enable_gpio)) { ++ ret = PTR_ERR(hdmi->enable_gpio); ++ dev_err(hdmi->dev, "failed to request enable GPIO: %d\n", ret); ++ return ret; ++ } ++ ++ hdmi->skip_check_420_mode = ++ of_property_read_bool(np, "skip-check-420-mode"); ++ + return 0; + } + +@@ -283,9 +1341,114 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, + + return MODE_BAD; + } ++/* [CC:] enable downstream mode_valid() */ ++// static enum drm_mode_status ++// dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, void *data, ++// const struct drm_display_info *info, ++// const struct drm_display_mode *mode) ++// { ++// struct drm_encoder *encoder = connector->encoder; ++// enum drm_mode_status status = MODE_OK; ++// struct drm_device *dev = connector->dev; ++// struct rockchip_drm_private *priv = dev->dev_private; ++// struct drm_crtc *crtc; ++// struct rockchip_hdmi *hdmi; ++// ++// /* ++// * Pixel clocks we support are always < 2GHz and so fit in an ++// * int. We should make sure source rate does too so we don't get ++// * overflow when we multiply by 1000. ++// */ ++// if (mode->clock > INT_MAX / 1000) ++// return MODE_BAD; ++// ++// if (!encoder) { ++// const struct drm_connector_helper_funcs *funcs; ++// ++// funcs = connector->helper_private; ++// if (funcs->atomic_best_encoder) ++// encoder = funcs->atomic_best_encoder(connector, ++// connector->state); ++// else ++// encoder = funcs->best_encoder(connector); ++// } ++// ++// if (!encoder || !encoder->possible_crtcs) ++// return MODE_BAD; ++// ++// hdmi = to_rockchip_hdmi(encoder); ++// ++// /* ++// * If sink max TMDS clock < 340MHz, we should check the mode pixel ++// * clock > 340MHz is YCbCr420 or not and whether the platform supports ++// * YCbCr420. ++// */ ++// if (!hdmi->skip_check_420_mode) { ++// if (mode->clock > 340000 && ++// connector->display_info.max_tmds_clock < 340000 && ++// (!drm_mode_is_420(&connector->display_info, mode) || ++// !connector->ycbcr_420_allowed)) ++// return MODE_BAD; ++// ++// if (hdmi->max_tmdsclk <= 340000 && mode->clock > 340000 && ++// !drm_mode_is_420(&connector->display_info, mode)) ++// return MODE_BAD; ++// }; ++// ++// if (hdmi->phy) { ++// if (hdmi->is_hdmi_qp) ++// phy_set_bus_width(hdmi->phy, mode->clock * 10); ++// else ++// phy_set_bus_width(hdmi->phy, 8); ++// } ++// ++// /* ++// * ensure all drm display mode can work, if someone want support more ++// * resolutions, please limit the possible_crtc, only connect to ++// * needed crtc. ++// */ ++// drm_for_each_crtc(crtc, connector->dev) { ++// int pipe = drm_crtc_index(crtc); ++// const struct rockchip_crtc_funcs *funcs = ++// priv->crtc_funcs[pipe]; ++// ++// if (!(encoder->possible_crtcs & drm_crtc_mask(crtc))) ++// continue; ++// if (!funcs || !funcs->mode_valid) ++// continue; ++// ++// status = funcs->mode_valid(crtc, mode, ++// DRM_MODE_CONNECTOR_HDMIA); ++// if (status != MODE_OK) ++// return status; ++// } ++// ++// return status; ++// } ++// + + static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) + { ++ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ // struct drm_crtc *crtc = encoder->crtc; ++ // struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); ++ // ++ // if (crtc->state->active_changed) { ++ // if (hdmi->plat_data->split_mode) { ++ // s->output_if &= ~(VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1); ++ // } else { ++ // if (!hdmi->id) ++ // s->output_if &= ~VOP_OUTPUT_IF_HDMI1; ++ // else ++ // s->output_if &= ~VOP_OUTPUT_IF_HDMI0; ++ // } ++ // } ++ /* ++ * when plug out hdmi it will be switch cvbs and then phy bus width ++ * must be set as 8 ++ */ ++ if (hdmi->phy) ++ phy_set_bus_width(hdmi->phy, 8); + } + + static bool +@@ -301,6 +1464,27 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *adj_mode) + { + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ struct drm_crtc *crtc; ++ struct rockchip_crtc_state *s; ++ ++ if (!encoder->crtc) ++ return; ++ crtc = encoder->crtc; ++ ++ if (!crtc->state) ++ return; ++ s = to_rockchip_crtc_state(crtc->state); ++ ++ if (!s) ++ return; ++ ++ if (hdmi->is_hdmi_qp) { ++ // s->dsc_enable = 0; ++ // if (hdmi->link_cfg.dsc_mode) ++ // dw_hdmi_qp_dsc_configure(hdmi, s, crtc->state); ++ ++ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); ++ } + + clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000); + } +@@ -308,14 +1492,25 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, + static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) + { + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ struct drm_crtc *crtc = encoder->crtc; + u32 val; ++ int mux; + int ret; + ++ if (WARN_ON(!crtc || !crtc->state)) ++ return; ++ ++ if (hdmi->phy) ++ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); ++ ++ clk_set_rate(hdmi->ref_clk, ++ crtc->state->adjusted_mode.crtc_clock * 1000); ++ + if (hdmi->chip_data->lcdsel_grf_reg < 0) + return; + +- ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); +- if (ret) ++ mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); ++ if (mux) + val = hdmi->chip_data->lcdsel_lit; + else + val = hdmi->chip_data->lcdsel_big; +@@ -330,24 +1525,992 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) + if (ret != 0) + DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret); + ++ if (hdmi->chip_data->lcdsel_grf_reg == RK3288_GRF_SOC_CON6) { ++ struct rockchip_crtc_state *s = ++ to_rockchip_crtc_state(crtc->state); ++ u32 mode_mask = mux ? RK3288_HDMI_LCDC1_YUV420 : ++ RK3288_HDMI_LCDC0_YUV420; ++ ++ if (s->output_mode == ROCKCHIP_OUT_MODE_YUV420) ++ val = HIWORD_UPDATE(mode_mask, mode_mask); ++ else ++ val = HIWORD_UPDATE(0, mode_mask); ++ ++ regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON16, val); ++ } ++ + clk_disable_unprepare(hdmi->grf_clk); + DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n", + ret ? "LIT" : "BIG"); + } + +-static int +-dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, +- struct drm_crtc_state *crtc_state, +- struct drm_connector_state *conn_state) ++static void rk3588_set_link_mode(struct rockchip_hdmi *hdmi) + { +- struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); ++ int val; ++ bool is_hdmi0; + +- s->output_mode = ROCKCHIP_OUT_MODE_AAAA; +- s->output_type = DRM_MODE_CONNECTOR_HDMIA; ++ if (!hdmi->id) ++ is_hdmi0 = true; ++ else ++ is_hdmi0 = false; ++ ++ if (!hdmi->link_cfg.frl_mode) { ++ val = HIWORD_UPDATE(0, RK3588_HDMI21_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); ++ ++ val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ ++ return; ++ } ++ ++ val = HIWORD_UPDATE(RK3588_HDMI21_MASK, RK3588_HDMI21_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); ++ ++ if (hdmi->link_cfg.dsc_mode) { ++ val = HIWORD_UPDATE(RK3588_COMPRESS_MODE_MASK | RK3588_COMPRESSED_DATA, ++ RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ } else { ++ val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ } ++} ++ ++static void rk3588_set_color_format(struct rockchip_hdmi *hdmi, u64 bus_format, ++ u32 depth) ++{ ++ u32 val = 0; ++ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ val = HIWORD_UPDATE(0, RK3588_COLOR_FORMAT_MASK); ++ break; ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ val = HIWORD_UPDATE(RK3588_YUV420, RK3588_COLOR_FORMAT_MASK); ++ break; ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ val = HIWORD_UPDATE(RK3588_YUV444, RK3588_COLOR_FORMAT_MASK); ++ break; ++ default: ++ dev_err(hdmi->dev, "can't set correct color format\n"); ++ return; ++ } ++ ++ if (hdmi->link_cfg.dsc_mode) ++ val = HIWORD_UPDATE(RK3588_COMPRESSED_DATA, RK3588_COLOR_FORMAT_MASK); ++ ++ if (depth == 8) ++ val |= HIWORD_UPDATE(RK3588_8BPC, RK3588_COLOR_DEPTH_MASK); ++ else ++ val |= HIWORD_UPDATE(RK3588_10BPC, RK3588_COLOR_DEPTH_MASK); ++ ++ if (!hdmi->id) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++} ++ ++static void rk3588_set_grf_cfg(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ int color_depth; ++ ++ rk3588_set_link_mode(hdmi); ++ color_depth = hdmi_bus_fmt_color_depth(hdmi->bus_format); ++ rk3588_set_color_format(hdmi, hdmi->bus_format, color_depth); ++} ++ ++static void ++dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, ++ struct drm_crtc_state *crtc_state, ++ struct rockchip_hdmi *hdmi, ++ unsigned int *color_format, ++ unsigned int *output_mode, ++ unsigned long *bus_format, ++ unsigned int *bus_width, ++ unsigned long *enc_out_encoding, ++ unsigned int *eotf) ++{ ++ struct drm_display_info *info = &conn_state->connector->display_info; ++ struct drm_display_mode mode; ++ struct hdr_output_metadata *hdr_metadata; ++ u32 vic; ++ unsigned long tmdsclock, pixclock; ++ unsigned int color_depth; ++ bool support_dc = false; ++ bool sink_is_hdmi = true; ++ u32 max_tmds_clock = info->max_tmds_clock; ++ int output_eotf; ++ ++ drm_mode_copy(&mode, &crtc_state->mode); ++ pixclock = mode.crtc_clock; ++ // if (hdmi->plat_data->split_mode) { ++ // drm_mode_convert_to_origin_mode(&mode); ++ // pixclock /= 2; ++ // } ++ ++ vic = drm_match_cea_mode(&mode); ++ ++ if (!hdmi->is_hdmi_qp) ++ sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi); ++ ++ *color_format = RK_IF_FORMAT_RGB; ++ ++ switch (hdmi->hdmi_output) { ++ case RK_IF_FORMAT_YCBCR_HQ: ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *color_format = RK_IF_FORMAT_YCBCR444; ++ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ else if (conn_state->connector->ycbcr_420_allowed && ++ drm_mode_is_420(info, &mode) && ++ (pixclock >= 594000 && !hdmi->is_hdmi_qp)) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ break; ++ case RK_IF_FORMAT_YCBCR_LQ: ++ if (conn_state->connector->ycbcr_420_allowed && ++ drm_mode_is_420(info, &mode) && pixclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *color_format = RK_IF_FORMAT_YCBCR444; ++ break; ++ case RK_IF_FORMAT_YCBCR420: ++ if (conn_state->connector->ycbcr_420_allowed && ++ drm_mode_is_420(info, &mode) && pixclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ break; ++ case RK_IF_FORMAT_YCBCR422: ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ break; ++ case RK_IF_FORMAT_YCBCR444: ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *color_format = RK_IF_FORMAT_YCBCR444; ++ break; ++ case RK_IF_FORMAT_RGB: ++ default: ++ break; ++ } ++ ++ if (*color_format == RK_IF_FORMAT_RGB && ++ info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) ++ support_dc = true; ++ if (*color_format == RK_IF_FORMAT_YCBCR444 && ++ info->edid_hdmi_rgb444_dc_modes & ++ (DRM_EDID_HDMI_DC_Y444 | DRM_EDID_HDMI_DC_30)) ++ support_dc = true; ++ if (*color_format == RK_IF_FORMAT_YCBCR422) ++ support_dc = true; ++ if (*color_format == RK_IF_FORMAT_YCBCR420 && ++ info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) ++ support_dc = true; ++ ++ if (hdmi->colordepth > 8 && support_dc) ++ color_depth = 10; ++ else ++ color_depth = 8; ++ ++ if (!sink_is_hdmi) { ++ *color_format = RK_IF_FORMAT_RGB; ++ color_depth = 8; ++ } ++ ++ *eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR; ++ if (conn_state->hdr_output_metadata) { ++ hdr_metadata = (struct hdr_output_metadata *) ++ conn_state->hdr_output_metadata->data; ++ output_eotf = hdr_metadata->hdmi_metadata_type1.eotf; ++ if (output_eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR && ++ output_eotf <= HDMI_EOTF_BT_2100_HLG) ++ *eotf = output_eotf; ++ } ++ ++ hdmi->colorimetry = conn_state->colorspace; ++ ++ if ((*eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR && ++ conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf & ++ BIT(*eotf)) || ((hdmi->colorimetry >= DRM_MODE_COLORIMETRY_BT2020_CYCC) && ++ (hdmi->colorimetry <= DRM_MODE_COLORIMETRY_BT2020_YCC))) ++ *enc_out_encoding = V4L2_YCBCR_ENC_BT2020; ++ else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) || ++ (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) ++ *enc_out_encoding = V4L2_YCBCR_ENC_601; ++ else ++ *enc_out_encoding = V4L2_YCBCR_ENC_709; ++ ++ if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { ++ /* BT2020 require color depth at lest 10bit */ ++ color_depth = 10; ++ /* We prefer use YCbCr422 to send 10bit */ ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ if (hdmi->is_hdmi_qp) { ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR420) { ++ if (mode.clock >= 340000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ else ++ *color_format = RK_IF_FORMAT_RGB; ++ } else { ++ *color_format = RK_IF_FORMAT_RGB; ++ } ++ } ++ } ++ ++ if (mode.flags & DRM_MODE_FLAG_DBLCLK) ++ pixclock *= 2; ++ if ((mode.flags & DRM_MODE_FLAG_3D_MASK) == ++ DRM_MODE_FLAG_3D_FRAME_PACKING) ++ pixclock *= 2; ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR422 || color_depth == 8) ++ tmdsclock = pixclock; ++ else ++ tmdsclock = pixclock * (color_depth) / 8; ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR420) ++ tmdsclock /= 2; ++ ++ /* XXX: max_tmds_clock of some sink is 0, we think it is 340MHz. */ ++ if (!max_tmds_clock) ++ max_tmds_clock = 340000; ++ ++ max_tmds_clock = min(max_tmds_clock, hdmi->max_tmdsclk); ++ ++ if ((tmdsclock > max_tmds_clock) && !hdmi->is_hdmi_qp) { ++ if (max_tmds_clock >= 594000) { ++ color_depth = 8; ++ } else if (max_tmds_clock > 340000) { ++ if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ } else { ++ color_depth = 8; ++ if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ } ++ } ++ ++ if (hdmi->is_hdmi_qp) { ++ if (mode.clock >= 340000) { ++ if (drm_mode_is_420(info, &mode)) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ else ++ *color_format = RK_IF_FORMAT_RGB; ++ } else if (tmdsclock > max_tmds_clock) { ++ color_depth = 8; ++ if (drm_mode_is_420(info, &mode)) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ } ++ } ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR420) { ++ *output_mode = ROCKCHIP_OUT_MODE_YUV420; ++ if (color_depth > 8) ++ *bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30; ++ else ++ *bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24; ++ *bus_width = color_depth / 2; ++ } else { ++ *output_mode = ROCKCHIP_OUT_MODE_AAAA; ++ if (color_depth > 8) { ++ if (*color_format != RK_IF_FORMAT_RGB && ++ !hdmi->unsupported_yuv_input) ++ *bus_format = MEDIA_BUS_FMT_YUV10_1X30; ++ else ++ *bus_format = MEDIA_BUS_FMT_RGB101010_1X30; ++ } else { ++ if (*color_format != RK_IF_FORMAT_RGB && ++ !hdmi->unsupported_yuv_input) ++ *bus_format = MEDIA_BUS_FMT_YUV8_1X24; ++ else ++ *bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ } ++ if (*color_format == RK_IF_FORMAT_YCBCR422) ++ *bus_width = 8; ++ else ++ *bus_width = color_depth; ++ } ++ ++ hdmi->bus_format = *bus_format; ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR422) { ++ if (color_depth == 12) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; ++ else if (color_depth == 10) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; ++ else ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; ++ } else { ++ hdmi->output_bus_format = *bus_format; ++ } ++} ++ ++static bool ++dw_hdmi_rockchip_check_color(struct drm_connector_state *conn_state, ++ struct rockchip_hdmi *hdmi) ++{ ++ struct drm_crtc_state *crtc_state = conn_state->crtc->state; ++ unsigned int colorformat; ++ unsigned long bus_format; ++ unsigned long output_bus_format = hdmi->output_bus_format; ++ unsigned long enc_out_encoding = hdmi->enc_out_encoding; ++ unsigned int eotf, bus_width; ++ unsigned int output_mode; ++ ++ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi, ++ &colorformat, ++ &output_mode, &bus_format, &bus_width, ++ &hdmi->enc_out_encoding, &eotf); ++ ++ if (output_bus_format != hdmi->output_bus_format || ++ enc_out_encoding != hdmi->enc_out_encoding) ++ return true; ++ else ++ return false; ++} ++ ++static int ++dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, ++ struct drm_crtc_state *crtc_state, ++ struct drm_connector_state *conn_state) ++{ ++ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); ++ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ unsigned int colorformat, bus_width, tmdsclk; ++ struct drm_display_mode mode; ++ unsigned int output_mode; ++ unsigned long bus_format; ++ int color_depth; ++ bool secondary = false; ++ ++ /* ++ * There are two hdmi but only one encoder in split mode, ++ * so we need to check twice. ++ */ ++secondary: ++ drm_mode_copy(&mode, &crtc_state->mode); ++ ++ hdmi->vp_id = 0; ++ // hdmi->vp_id = s->vp_id; ++ // if (hdmi->plat_data->split_mode) ++ // drm_mode_convert_to_origin_mode(&mode); ++ ++ int eotf; ++ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi, ++ &colorformat, ++ &output_mode, &bus_format, &bus_width, ++ // &hdmi->enc_out_encoding, &s->eotf); ++ &hdmi->enc_out_encoding, &eotf); ++ ++ s->bus_format = bus_format; ++ if (hdmi->is_hdmi_qp) { ++ color_depth = hdmi_bus_fmt_color_depth(bus_format); ++ tmdsclk = hdmi_get_tmdsclock(hdmi, crtc_state->mode.clock); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format)) ++ tmdsclk /= 2; ++ hdmi_select_link_config(hdmi, crtc_state, tmdsclk); ++ ++ if (hdmi->link_cfg.frl_mode) { ++ gpiod_set_value(hdmi->enable_gpio, 0); ++ /* in the current version, support max 40G frl */ ++ if (hdmi->link_cfg.rate_per_lane >= 10) { ++ hdmi->link_cfg.frl_lanes = 4; ++ hdmi->link_cfg.rate_per_lane = 10; ++ } ++ bus_width = hdmi->link_cfg.frl_lanes * ++ hdmi->link_cfg.rate_per_lane * 1000000; ++ /* 10 bit color depth and frl mode */ ++ if (color_depth == 10) ++ bus_width |= ++ COLOR_DEPTH_10BIT | HDMI_FRL_MODE; ++ else ++ bus_width |= HDMI_FRL_MODE; ++ } else { ++ gpiod_set_value(hdmi->enable_gpio, 1); ++ bus_width = hdmi_get_tmdsclock(hdmi, mode.clock * 10); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format)) ++ bus_width /= 2; ++ ++ if (color_depth == 10) ++ bus_width |= COLOR_DEPTH_10BIT; ++ } ++ } ++ ++ hdmi->phy_bus_width = bus_width; ++ ++ if (hdmi->phy) ++ phy_set_bus_width(hdmi->phy, bus_width); ++ ++ s->output_type = DRM_MODE_CONNECTOR_HDMIA; ++ // s->tv_state = &conn_state->tv; ++ // ++ // if (hdmi->plat_data->split_mode) { ++ // s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE; ++ // if (hdmi->plat_data->right && hdmi->id) ++ // s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP; ++ // s->output_if |= VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1; ++ // } else { ++ // if (!hdmi->id) ++ // s->output_if |= VOP_OUTPUT_IF_HDMI0; ++ // else ++ // s->output_if |= VOP_OUTPUT_IF_HDMI1; ++ // } ++ ++ s->output_mode = output_mode; ++ hdmi->bus_format = s->bus_format; ++ ++ if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_BT2020) ++ s->color_space = V4L2_COLORSPACE_BT2020; ++ else if (colorformat == RK_IF_FORMAT_RGB) ++ s->color_space = V4L2_COLORSPACE_DEFAULT; ++ else if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_709) ++ s->color_space = V4L2_COLORSPACE_REC709; ++ else ++ s->color_space = V4L2_COLORSPACE_SMPTE170M; ++ ++ if (hdmi->plat_data->split_mode && !secondary) { ++ hdmi = rockchip_hdmi_find_by_id(hdmi->dev->driver, !hdmi->id); ++ secondary = true; ++ goto secondary; ++ } + + return 0; + } + ++static unsigned long ++dw_hdmi_rockchip_get_input_bus_format(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->bus_format; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_output_bus_format(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->output_bus_format; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_enc_in_encoding(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->enc_out_encoding; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_enc_out_encoding(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->enc_out_encoding; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_quant_range(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->hdmi_quant_range; ++} ++ ++static struct drm_property * ++dw_hdmi_rockchip_get_hdr_property(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->hdr_panel_metadata_property; ++} ++ ++static struct drm_property_blob * ++dw_hdmi_rockchip_get_hdr_blob(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->hdr_panel_blob_ptr; ++} ++ ++static bool ++dw_hdmi_rockchip_get_color_changed(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ bool ret = false; ++ ++ if (hdmi->color_changed) ++ ret = true; ++ hdmi->color_changed = 0; ++ ++ return ret; ++} ++ ++#if 0 ++static int ++dw_hdmi_rockchip_get_edid_dsc_info(void *data, struct edid *edid) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ if (!edid) ++ return -EINVAL; ++ ++ return rockchip_drm_parse_cea_ext(&hdmi->dsc_cap, ++ &hdmi->max_frl_rate_per_lane, ++ &hdmi->max_lanes, edid); ++} ++ ++static int ++dw_hdmi_rockchip_get_next_hdr_data(void *data, struct edid *edid, ++ struct drm_connector *connector) ++{ ++ int ret; ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct next_hdr_sink_data *sink_data = &hdmi->next_hdr_data; ++ size_t size = sizeof(*sink_data); ++ struct drm_property *property = hdmi->next_hdr_sink_data_property; ++ struct drm_property_blob *blob = hdmi->hdr_panel_blob_ptr; ++ ++ if (!edid) ++ return -EINVAL; ++ ++ rockchip_drm_parse_next_hdr(sink_data, edid); ++ ++ ret = drm_property_replace_global_blob(connector->dev, &blob, size, sink_data, ++ &connector->base, property); ++ ++ return ret; ++}; ++#endif ++ ++static ++struct dw_hdmi_link_config *dw_hdmi_rockchip_get_link_cfg(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return &hdmi->link_cfg; ++} ++ ++#if 0 ++static const struct drm_prop_enum_list color_depth_enum_list[] = { ++ { 0, "Automatic" }, /* Prefer highest color depth */ ++ { 8, "24bit" }, ++ { 10, "30bit" }, ++}; ++ ++static const struct drm_prop_enum_list drm_hdmi_output_enum_list[] = { ++ { RK_IF_FORMAT_RGB, "rgb" }, ++ { RK_IF_FORMAT_YCBCR444, "ycbcr444" }, ++ { RK_IF_FORMAT_YCBCR422, "ycbcr422" }, ++ { RK_IF_FORMAT_YCBCR420, "ycbcr420" }, ++ { RK_IF_FORMAT_YCBCR_HQ, "ycbcr_high_subsampling" }, ++ { RK_IF_FORMAT_YCBCR_LQ, "ycbcr_low_subsampling" }, ++ { RK_IF_FORMAT_MAX, "invalid_output" }, ++}; ++ ++static const struct drm_prop_enum_list quant_range_enum_list[] = { ++ { HDMI_QUANTIZATION_RANGE_DEFAULT, "default" }, ++ { HDMI_QUANTIZATION_RANGE_LIMITED, "limit" }, ++ { HDMI_QUANTIZATION_RANGE_FULL, "full" }, ++}; ++ ++static const struct drm_prop_enum_list output_hdmi_dvi_enum_list[] = { ++ { 0, "auto" }, ++ { 1, "force_hdmi" }, ++ { 2, "force_dvi" }, ++}; ++ ++static const struct drm_prop_enum_list output_type_cap_list[] = { ++ { 0, "DVI" }, ++ { 1, "HDMI" }, ++}; ++#endif ++ ++static void ++dw_hdmi_rockchip_attach_properties(struct drm_connector *connector, ++ unsigned int color, int version, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct drm_property *prop; ++ // struct rockchip_drm_private *private = connector->dev->dev_private; ++ ++ switch (color) { ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ hdmi->hdmi_output = RK_IF_FORMAT_RGB; ++ hdmi->colordepth = 10; ++ break; ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444; ++ hdmi->colordepth = 8; ++ break; ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444; ++ hdmi->colordepth = 10; ++ break; ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422; ++ hdmi->colordepth = 10; ++ break; ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422; ++ hdmi->colordepth = 8; ++ break; ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420; ++ hdmi->colordepth = 8; ++ break; ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420; ++ hdmi->colordepth = 10; ++ break; ++ default: ++ hdmi->hdmi_output = RK_IF_FORMAT_RGB; ++ hdmi->colordepth = 8; ++ } ++ ++ hdmi->bus_format = color; ++ ++ if (hdmi->hdmi_output == RK_IF_FORMAT_YCBCR422) { ++ if (hdmi->colordepth == 12) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; ++ else if (hdmi->colordepth == 10) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; ++ else ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; ++ } else { ++ hdmi->output_bus_format = hdmi->bus_format; ++ } ++ ++#if 0 ++ /* RK3368 does not support deep color mode */ ++ if (!hdmi->color_depth_property && !hdmi->unsupported_deep_color) { ++ prop = drm_property_create_enum(connector->dev, 0, ++ RK_IF_PROP_COLOR_DEPTH, ++ color_depth_enum_list, ++ ARRAY_SIZE(color_depth_enum_list)); ++ if (prop) { ++ hdmi->color_depth_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ } ++ ++ prop = drm_property_create_enum(connector->dev, 0, RK_IF_PROP_COLOR_FORMAT, ++ drm_hdmi_output_enum_list, ++ ARRAY_SIZE(drm_hdmi_output_enum_list)); ++ if (prop) { ++ hdmi->hdmi_output_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_range(connector->dev, 0, ++ RK_IF_PROP_COLOR_DEPTH_CAPS, ++ 0, 0xff); ++ if (prop) { ++ hdmi->colordepth_capacity = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_range(connector->dev, 0, ++ RK_IF_PROP_COLOR_FORMAT_CAPS, ++ 0, 0xf); ++ if (prop) { ++ hdmi->outputmode_capacity = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create(connector->dev, ++ DRM_MODE_PROP_BLOB | ++ DRM_MODE_PROP_IMMUTABLE, ++ "HDR_PANEL_METADATA", 0); ++ if (prop) { ++ hdmi->hdr_panel_metadata_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create(connector->dev, ++ DRM_MODE_PROP_BLOB | ++ DRM_MODE_PROP_IMMUTABLE, ++ "NEXT_HDR_SINK_DATA", 0); ++ if (prop) { ++ hdmi->next_hdr_sink_data_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_bool(connector->dev, DRM_MODE_PROP_IMMUTABLE, ++ "USER_SPLIT_MODE"); ++ if (prop) { ++ hdmi->user_split_mode_prop = prop; ++ drm_object_attach_property(&connector->base, prop, ++ hdmi->user_split_mode ? 1 : 0); ++ } ++ ++ if (!hdmi->is_hdmi_qp) { ++ prop = drm_property_create_enum(connector->dev, 0, ++ "output_hdmi_dvi", ++ output_hdmi_dvi_enum_list, ++ ARRAY_SIZE(output_hdmi_dvi_enum_list)); ++ if (prop) { ++ hdmi->output_hdmi_dvi = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_enum(connector->dev, 0, ++ "output_type_capacity", ++ output_type_cap_list, ++ ARRAY_SIZE(output_type_cap_list)); ++ if (prop) { ++ hdmi->output_type_capacity = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_enum(connector->dev, 0, ++ "hdmi_quant_range", ++ quant_range_enum_list, ++ ARRAY_SIZE(quant_range_enum_list)); ++ if (prop) { ++ hdmi->quant_range = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ } ++#endif ++ ++ prop = connector->dev->mode_config.hdr_output_metadata_property; ++ if (version >= 0x211a || hdmi->is_hdmi_qp) ++ drm_object_attach_property(&connector->base, prop, 0); ++ ++ if (!drm_mode_create_hdmi_colorspace_property(connector, 0)) ++ drm_object_attach_property(&connector->base, ++ connector->colorspace_property, 0); ++ ++#if 0 ++ // [CC:] if this is not needed, also drop connector_id_prop ++ if (!private->connector_id_prop) ++ private->connector_id_prop = drm_property_create_range(connector->dev, ++ DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE, ++ "CONNECTOR_ID", 0, 0xf); ++ if (private->connector_id_prop) ++ drm_object_attach_property(&connector->base, private->connector_id_prop, hdmi->id); ++#endif ++} ++ ++static void ++dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ if (hdmi->color_depth_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->color_depth_property); ++ hdmi->color_depth_property = NULL; ++ } ++ ++ if (hdmi->hdmi_output_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->hdmi_output_property); ++ hdmi->hdmi_output_property = NULL; ++ } ++ ++ if (hdmi->colordepth_capacity) { ++ drm_property_destroy(connector->dev, ++ hdmi->colordepth_capacity); ++ hdmi->colordepth_capacity = NULL; ++ } ++ ++ if (hdmi->outputmode_capacity) { ++ drm_property_destroy(connector->dev, ++ hdmi->outputmode_capacity); ++ hdmi->outputmode_capacity = NULL; ++ } ++ ++ if (hdmi->quant_range) { ++ drm_property_destroy(connector->dev, ++ hdmi->quant_range); ++ hdmi->quant_range = NULL; ++ } ++ ++ if (hdmi->hdr_panel_metadata_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->hdr_panel_metadata_property); ++ hdmi->hdr_panel_metadata_property = NULL; ++ } ++ ++ if (hdmi->next_hdr_sink_data_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->next_hdr_sink_data_property); ++ hdmi->next_hdr_sink_data_property = NULL; ++ } ++ ++ if (hdmi->output_hdmi_dvi) { ++ drm_property_destroy(connector->dev, ++ hdmi->output_hdmi_dvi); ++ hdmi->output_hdmi_dvi = NULL; ++ } ++ ++ if (hdmi->output_type_capacity) { ++ drm_property_destroy(connector->dev, ++ hdmi->output_type_capacity); ++ hdmi->output_type_capacity = NULL; ++ } ++ ++ if (hdmi->user_split_mode_prop) { ++ drm_property_destroy(connector->dev, ++ hdmi->user_split_mode_prop); ++ hdmi->user_split_mode_prop = NULL; ++ } ++} ++ ++static int ++dw_hdmi_rockchip_set_property(struct drm_connector *connector, ++ struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 val, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct drm_mode_config *config = &connector->dev->mode_config; ++ ++ if (property == hdmi->color_depth_property) { ++ hdmi->colordepth = val; ++ /* If hdmi is disconnected, state->crtc is null */ ++ if (!state->crtc) ++ return 0; ++ if (dw_hdmi_rockchip_check_color(state, hdmi)) ++ hdmi->color_changed++; ++ return 0; ++ } else if (property == hdmi->hdmi_output_property) { ++ hdmi->hdmi_output = val; ++ if (!state->crtc) ++ return 0; ++ if (dw_hdmi_rockchip_check_color(state, hdmi)) ++ hdmi->color_changed++; ++ return 0; ++ } else if (property == hdmi->quant_range) { ++ u64 quant_range = hdmi->hdmi_quant_range; ++ ++ hdmi->hdmi_quant_range = val; ++ if (quant_range != hdmi->hdmi_quant_range) ++ dw_hdmi_set_quant_range(hdmi->hdmi); ++ return 0; ++ } else if (property == config->hdr_output_metadata_property) { ++ return 0; ++ } else if (property == hdmi->output_hdmi_dvi) { ++ if (hdmi->force_output != val) ++ hdmi->color_changed++; ++ hdmi->force_output = val; ++ dw_hdmi_set_output_type(hdmi->hdmi, val); ++ return 0; ++ } else if (property == hdmi->colordepth_capacity) { ++ return 0; ++ } else if (property == hdmi->outputmode_capacity) { ++ return 0; ++ } else if (property == hdmi->output_type_capacity) { ++ return 0; ++ } ++ ++ DRM_ERROR("Unknown property [PROP:%d:%s]\n", ++ property->base.id, property->name); ++ ++ return -EINVAL; ++} ++ ++static int ++dw_hdmi_rockchip_get_property(struct drm_connector *connector, ++ const struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 *val, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct drm_display_info *info = &connector->display_info; ++ struct drm_mode_config *config = &connector->dev->mode_config; ++ ++ if (property == hdmi->color_depth_property) { ++ *val = hdmi->colordepth; ++ return 0; ++ } else if (property == hdmi->hdmi_output_property) { ++ *val = hdmi->hdmi_output; ++ return 0; ++ } else if (property == hdmi->colordepth_capacity) { ++ *val = BIT(RK_IF_DEPTH_8); ++ /* RK3368 only support 8bit */ ++ if (hdmi->unsupported_deep_color) ++ return 0; ++ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) ++ *val |= BIT(RK_IF_DEPTH_10); ++ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36) ++ *val |= BIT(RK_IF_DEPTH_12); ++ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_48) ++ *val |= BIT(RK_IF_DEPTH_16); ++ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) ++ *val |= BIT(RK_IF_DEPTH_420_10); ++ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) ++ *val |= BIT(RK_IF_DEPTH_420_12); ++ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48) ++ *val |= BIT(RK_IF_DEPTH_420_16); ++ return 0; ++ } else if (property == hdmi->outputmode_capacity) { ++ *val = BIT(RK_IF_FORMAT_RGB); ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *val |= BIT(RK_IF_FORMAT_YCBCR444); ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *val |= BIT(RK_IF_FORMAT_YCBCR422); ++ if (connector->ycbcr_420_allowed && ++ info->color_formats & DRM_COLOR_FORMAT_YCBCR420) ++ *val |= BIT(RK_IF_FORMAT_YCBCR420); ++ return 0; ++ } else if (property == hdmi->quant_range) { ++ *val = hdmi->hdmi_quant_range; ++ return 0; ++ } else if (property == config->hdr_output_metadata_property) { ++ *val = state->hdr_output_metadata ? ++ state->hdr_output_metadata->base.id : 0; ++ return 0; ++ } else if (property == hdmi->output_hdmi_dvi) { ++ *val = hdmi->force_output; ++ return 0; ++ } else if (property == hdmi->output_type_capacity) { ++ *val = dw_hdmi_get_output_type_cap(hdmi->hdmi); ++ return 0; ++ } else if (property == hdmi->user_split_mode_prop) { ++ *val = hdmi->user_split_mode; ++ return 0; ++ } ++ ++ DRM_ERROR("Unknown property [PROP:%d:%s]\n", ++ property->base.id, property->name); ++ ++ return -EINVAL; ++} ++ ++static const struct dw_hdmi_property_ops dw_hdmi_rockchip_property_ops = { ++ .attach_properties = dw_hdmi_rockchip_attach_properties, ++ .destroy_properties = dw_hdmi_rockchip_destroy_properties, ++ .set_property = dw_hdmi_rockchip_set_property, ++ .get_property = dw_hdmi_rockchip_get_property, ++}; ++ + static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = { + .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup, + .mode_set = dw_hdmi_rockchip_encoder_mode_set, +@@ -356,20 +2519,24 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun + .atomic_check = dw_hdmi_rockchip_encoder_atomic_check, + }; + +-static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, +- const struct drm_display_info *display, +- const struct drm_display_mode *mode) ++static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) + { + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + +- return phy_power_on(hdmi->phy); ++ while (hdmi->phy->power_count > 0) ++ phy_power_off(hdmi->phy); + } + +-static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) ++static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, ++ const struct drm_display_info *display, ++ const struct drm_display_mode *mode) + { + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + +- phy_power_off(hdmi->phy); ++ dw_hdmi_rockchip_genphy_disable(dw_hdmi, data); ++ dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display); ++ ++ return phy_power_on(hdmi->phy); + } + + static void dw_hdmi_rk3228_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) +@@ -436,6 +2603,90 @@ static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) + RK3328_HDMI_HPD_IOE)); + } + ++static void dw_hdmi_qp_rockchip_phy_disable(struct dw_hdmi_qp *dw_hdmi, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ while (hdmi->phy->power_count > 0) ++ phy_power_off(hdmi->phy); ++} ++ ++static int dw_hdmi_qp_rockchip_genphy_init(struct dw_hdmi_qp *dw_hdmi, void *data, ++ struct drm_display_mode *mode) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ dw_hdmi_qp_rockchip_phy_disable(dw_hdmi, data); ++ ++ return phy_power_on(hdmi->phy); ++} ++ ++static enum drm_connector_status ++dw_hdmi_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) ++{ ++ u32 val; ++ int ret; ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val); ++ ++ if (!hdmi->id) { ++ if (val & RK3588_HDMI0_LEVEL_INT) { ++ hdmi->hpd_stat = true; ++ ret = connector_status_connected; ++ } else { ++ hdmi->hpd_stat = false; ++ ret = connector_status_disconnected; ++ } ++ } else { ++ if (val & RK3588_HDMI1_LEVEL_INT) { ++ hdmi->hpd_stat = true; ++ ret = connector_status_connected; ++ } else { ++ hdmi->hpd_stat = false; ++ ret = connector_status_disconnected; ++ } ++ } ++ ++ return ret; ++} ++ ++static void dw_hdmi_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ u32 val; ++ ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, ++ RK3588_HDMI0_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); ++ } else { ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, ++ RK3588_HDMI1_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); ++ } ++ ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++} ++ ++static void dw_hdmi_rk3588_phy_set_mode(struct dw_hdmi_qp *dw_hdmi, void *data, ++ u32 mode_mask, bool enable) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ if (!hdmi->phy) ++ return; ++ ++ /* set phy earc/frl mode */ ++ if (enable) ++ hdmi->phy_bus_width |= mode_mask; ++ else ++ hdmi->phy_bus_width &= ~mode_mask; ++ ++ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); ++} ++ + static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = { + .init = dw_hdmi_rockchip_genphy_init, + .disable = dw_hdmi_rockchip_genphy_disable, +@@ -525,6 +2776,30 @@ static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = { + .use_drm_infoframe = true, + }; + ++static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = { ++ .init = dw_hdmi_qp_rockchip_genphy_init, ++ .disable = dw_hdmi_qp_rockchip_phy_disable, ++ .read_hpd = dw_hdmi_rk3588_read_hpd, ++ .setup_hpd = dw_hdmi_rk3588_setup_hpd, ++ .set_mode = dw_hdmi_rk3588_phy_set_mode, ++}; ++ ++struct rockchip_hdmi_chip_data rk3588_hdmi_chip_data = { ++ .lcdsel_grf_reg = -1, ++ .ddc_en_reg = RK3588_GRF_VO1_CON3, ++ .split_mode = true, ++}; ++ ++static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = { ++ .phy_data = &rk3588_hdmi_chip_data, ++ .qp_phy_ops = &rk3588_hdmi_phy_ops, ++ .phy_name = "samsung_hdptx_phy", ++ .phy_force_vendor = true, ++ .ycbcr_420_allowed = true, ++ .is_hdmi_qp = true, ++ .use_drm_infoframe = true, ++}; ++ + static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3228-dw-hdmi", + .data = &rk3228_hdmi_drv_data +@@ -541,6 +2816,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3568-dw-hdmi", + .data = &rk3568_hdmi_drv_data + }, ++ { .compatible = "rockchip,rk3588-dw-hdmi", ++ .data = &rk3588_hdmi_drv_data ++ }, + {}, + }; + MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids); +@@ -550,44 +2828,103 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + { + struct platform_device *pdev = to_platform_device(dev); + struct dw_hdmi_plat_data *plat_data; +- const struct of_device_id *match; + struct drm_device *drm = data; + struct drm_encoder *encoder; + struct rockchip_hdmi *hdmi; ++ struct rockchip_hdmi *secondary; + int ret; ++ u32 val; + + if (!pdev->dev.of_node) + return -ENODEV; + +- hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); ++ hdmi = platform_get_drvdata(pdev); + if (!hdmi) + return -ENOMEM; + +- match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); +- plat_data = devm_kmemdup(&pdev->dev, match->data, +- sizeof(*plat_data), GFP_KERNEL); +- if (!plat_data) +- return -ENOMEM; ++ plat_data = hdmi->plat_data; ++ hdmi->drm_dev = drm; + +- hdmi->dev = &pdev->dev; +- hdmi->plat_data = plat_data; +- hdmi->chip_data = plat_data->phy_data; + plat_data->phy_data = hdmi; +- plat_data->priv_data = hdmi; +- encoder = &hdmi->encoder.encoder; ++ plat_data->get_input_bus_format = ++ dw_hdmi_rockchip_get_input_bus_format; ++ plat_data->get_output_bus_format = ++ dw_hdmi_rockchip_get_output_bus_format; ++ plat_data->get_enc_in_encoding = ++ dw_hdmi_rockchip_get_enc_in_encoding; ++ plat_data->get_enc_out_encoding = ++ dw_hdmi_rockchip_get_enc_out_encoding; ++ plat_data->get_quant_range = ++ dw_hdmi_rockchip_get_quant_range; ++ plat_data->get_hdr_property = ++ dw_hdmi_rockchip_get_hdr_property; ++ plat_data->get_hdr_blob = ++ dw_hdmi_rockchip_get_hdr_blob; ++ plat_data->get_color_changed = ++ dw_hdmi_rockchip_get_color_changed; ++ // plat_data->get_edid_dsc_info = ++ // dw_hdmi_rockchip_get_edid_dsc_info; ++ // plat_data->get_next_hdr_data = ++ // dw_hdmi_rockchip_get_next_hdr_data; ++ plat_data->get_link_cfg = dw_hdmi_rockchip_get_link_cfg; ++ plat_data->set_grf_cfg = rk3588_set_grf_cfg; ++ // plat_data->convert_to_split_mode = drm_mode_convert_to_split_mode; ++ // plat_data->convert_to_origin_mode = drm_mode_convert_to_origin_mode; ++ ++ plat_data->property_ops = &dw_hdmi_rockchip_property_ops; ++ ++ secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id); ++ /* If don't enable hdmi0 and hdmi1, we don't enable split mode */ ++ if (hdmi->chip_data->split_mode && secondary) { + +- encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); +- rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder, +- dev->of_node, 0, 0); ++ /* ++ * hdmi can only attach bridge and init encoder/connector in the ++ * last bind hdmi in split mode, or hdmi->hdmi_qp will not be initialized ++ * and plat_data->left/right will be null pointer. we must check if split ++ * mode is on and determine the sequence of hdmi bind. ++ */ ++ if (device_property_read_bool(dev, "split-mode") || ++ device_property_read_bool(secondary->dev, "split-mode")) { ++ plat_data->split_mode = true; ++ secondary->plat_data->split_mode = true; ++ if (!secondary->plat_data->first_screen) ++ plat_data->first_screen = true; ++ } ++ ++ if (device_property_read_bool(dev, "user-split-mode") || ++ device_property_read_bool(secondary->dev, "user-split-mode")) { ++ hdmi->user_split_mode = true; ++ secondary->user_split_mode = true; ++ } ++ } ++ ++ if (!plat_data->first_screen) { ++ encoder = &hdmi->encoder.encoder; ++ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); ++ rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder, ++ dev->of_node, 0, 0); ++ /* ++ * If we failed to find the CRTC(s) which this encoder is ++ * supposed to be connected to, it's because the CRTC has ++ * not been registered yet. Defer probing, and hope that ++ * the required CRTC is added later. ++ */ ++ if (encoder->possible_crtcs == 0) ++ return -EPROBE_DEFER; ++ ++ drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); ++ // [CC:] consider using drmm_simple_encoder_alloc() ++ drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); ++ } ++ ++ if (!plat_data->max_tmdsclk) ++ hdmi->max_tmdsclk = 594000; ++ else ++ hdmi->max_tmdsclk = plat_data->max_tmdsclk; + +- /* +- * If we failed to find the CRTC(s) which this encoder is +- * supposed to be connected to, it's because the CRTC has +- * not been registered yet. Defer probing, and hope that +- * the required CRTC is added later. +- */ +- if (encoder->possible_crtcs == 0) +- return -EPROBE_DEFER; ++ ++ hdmi->unsupported_yuv_input = plat_data->unsupported_yuv_input; ++ hdmi->unsupported_deep_color = plat_data->unsupported_deep_color; + + ret = rockchip_hdmi_parse_dt(hdmi); + if (ret) { +@@ -596,34 +2933,44 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + return ret; + } + +- hdmi->phy = devm_phy_optional_get(dev, "hdmi"); +- if (IS_ERR(hdmi->phy)) { +- ret = PTR_ERR(hdmi->phy); +- if (ret != -EPROBE_DEFER) +- DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); ++ ret = clk_prepare_enable(hdmi->aud_clk); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI aud_clk: %d\n", ret); + return ret; + } + +- ret = regulator_enable(hdmi->avdd_0v9); ++ ret = clk_prepare_enable(hdmi->hpd_clk); + if (ret) { +- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret); +- goto err_avdd_0v9; ++ dev_err(hdmi->dev, "Failed to enable HDMI hpd_clk: %d\n", ret); ++ return ret; + } + +- ret = regulator_enable(hdmi->avdd_1v8); ++ ret = clk_prepare_enable(hdmi->hclk_vo1); + if (ret) { +- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret); +- goto err_avdd_1v8; ++ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vo1: %d\n", ret); ++ return ret; + } + +- ret = clk_prepare_enable(hdmi->ref_clk); ++ ret = clk_prepare_enable(hdmi->earc_clk); + if (ret) { +- DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n", +- ret); +- goto err_clk; ++ dev_err(hdmi->dev, "Failed to enable HDMI earc_clk: %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(hdmi->hdmitx_ref); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI hdmitx_ref: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(hdmi->pclk); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI pclk: %d\n", ret); ++ return ret; + } + +- if (hdmi->chip_data == &rk3568_chip_data) { ++ if (hdmi->chip_data->ddc_en_reg == RK3568_GRF_VO_CON1) { + regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1, + HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK | + RK3568_HDMI_SCLIN_MSK, +@@ -631,12 +2978,131 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + RK3568_HDMI_SCLIN_MSK)); + } + +- drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); +- drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); ++ if (hdmi->is_hdmi_qp) { ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, ++ RK3588_HDMI0_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } else { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, ++ RK3588_HDMI1_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } ++ } + +- platform_set_drvdata(pdev, hdmi); ++ ret = clk_prepare_enable(hdmi->hclk_vio); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vio: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(hdmi->hclk_vop); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vop: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(hdmi->ref_clk); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n", ++ ret); ++ goto err_clk; ++ } ++ ++ ret = regulator_enable(hdmi->avdd_0v9); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret); ++ goto err_avdd_0v9; ++ } ++ ++ ret = regulator_enable(hdmi->avdd_1v8); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret); ++ goto err_avdd_1v8; ++ } ++ ++ if (!hdmi->id) ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK); ++ else ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ ++ if (hdmi->is_hdmi_qp) { ++ hdmi->hpd_irq = platform_get_irq(pdev, 4); ++ if (hdmi->hpd_irq < 0) ++ return hdmi->hpd_irq; ++ ++ ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq, ++ rockchip_hdmi_hardirq, ++ rockchip_hdmi_irq, ++ IRQF_SHARED, "dw-hdmi-qp-hpd", ++ hdmi); ++ if (ret) ++ return ret; ++ } ++ ++ hdmi->phy = devm_phy_optional_get(dev, "hdmi"); ++ if (IS_ERR(hdmi->phy)) { ++ hdmi->phy = devm_phy_optional_get(dev, "hdmi_phy"); ++ if (IS_ERR(hdmi->phy)) { ++ ret = PTR_ERR(hdmi->phy); ++ if (ret != -EPROBE_DEFER) ++ DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); ++ return ret; ++ } ++ } ++ ++ if (hdmi->is_hdmi_qp) { ++ // [CC:] do proper error handling, e.g. clk_disable_unprepare ++ hdmi->hdmi_qp = dw_hdmi_qp_bind(pdev, &hdmi->encoder.encoder, plat_data); ++ ++ if (IS_ERR(hdmi->hdmi_qp)) { ++ ret = PTR_ERR(hdmi->hdmi_qp); ++ drm_encoder_cleanup(&hdmi->encoder.encoder); ++ } ++ ++ // if (plat_data->connector) { ++ // hdmi->sub_dev.connector = plat_data->connector; ++ // hdmi->sub_dev.of_node = dev->of_node; ++ // rockchip_drm_register_sub_dev(&hdmi->sub_dev); ++ // } ++ ++ if (plat_data->split_mode && secondary) { ++ if (device_property_read_bool(dev, "split-mode")) { ++ plat_data->right = secondary->hdmi_qp; ++ secondary->plat_data->left = hdmi->hdmi_qp; ++ } else { ++ plat_data->left = secondary->hdmi_qp; ++ secondary->plat_data->right = hdmi->hdmi_qp; ++ } ++ } ++ ++ return ret; ++ } + +- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data); ++ hdmi->hdmi = dw_hdmi_bind(pdev, &hdmi->encoder.encoder, plat_data); + + /* + * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), +@@ -647,11 +3113,24 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + goto err_bind; + } + ++ // if (plat_data->connector) { ++ // hdmi->sub_dev.connector = plat_data->connector; ++ // hdmi->sub_dev.of_node = dev->of_node; ++ // rockchip_drm_register_sub_dev(&hdmi->sub_dev); ++ // } ++ + return 0; + + err_bind: +- drm_encoder_cleanup(encoder); ++ drm_encoder_cleanup(&hdmi->encoder.encoder); ++ clk_disable_unprepare(hdmi->aud_clk); + clk_disable_unprepare(hdmi->ref_clk); ++ clk_disable_unprepare(hdmi->hclk_vop); ++ clk_disable_unprepare(hdmi->hpd_clk); ++ clk_disable_unprepare(hdmi->hclk_vo1); ++ clk_disable_unprepare(hdmi->earc_clk); ++ clk_disable_unprepare(hdmi->hdmitx_ref); ++ clk_disable_unprepare(hdmi->pclk); + err_clk: + regulator_disable(hdmi->avdd_1v8); + err_avdd_1v8: +@@ -665,9 +3144,29 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, + { + struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + +- dw_hdmi_unbind(hdmi->hdmi); ++ if (hdmi->is_hdmi_qp) { ++ cancel_delayed_work(&hdmi->work); ++ flush_workqueue(hdmi->workqueue); ++ } ++ ++ // if (hdmi->sub_dev.connector) ++ // rockchip_drm_unregister_sub_dev(&hdmi->sub_dev); ++ // ++ if (hdmi->is_hdmi_qp) ++ dw_hdmi_qp_unbind(hdmi->hdmi_qp); ++ else ++ dw_hdmi_unbind(hdmi->hdmi); ++ + drm_encoder_cleanup(&hdmi->encoder.encoder); ++ ++ clk_disable_unprepare(hdmi->aud_clk); + clk_disable_unprepare(hdmi->ref_clk); ++ clk_disable_unprepare(hdmi->hclk_vop); ++ clk_disable_unprepare(hdmi->hpd_clk); ++ clk_disable_unprepare(hdmi->hclk_vo1); ++ clk_disable_unprepare(hdmi->earc_clk); ++ clk_disable_unprepare(hdmi->hdmitx_ref); ++ clk_disable_unprepare(hdmi->pclk); + + regulator_disable(hdmi->avdd_1v8); + regulator_disable(hdmi->avdd_0v9); +@@ -680,30 +3179,142 @@ static const struct component_ops dw_hdmi_rockchip_ops = { + + static int dw_hdmi_rockchip_probe(struct platform_device *pdev) + { ++ struct rockchip_hdmi *hdmi; ++ const struct of_device_id *match; ++ struct dw_hdmi_plat_data *plat_data; ++ int id; ++ ++ hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); ++ if (!hdmi) ++ return -ENOMEM; ++ ++ id = of_alias_get_id(pdev->dev.of_node, "hdmi"); ++ if (id < 0) ++ id = 0; ++ ++ hdmi->id = id; ++ hdmi->dev = &pdev->dev; ++ ++ match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); ++ plat_data = devm_kmemdup(&pdev->dev, match->data, ++ sizeof(*plat_data), GFP_KERNEL); ++ if (!plat_data) ++ return -ENOMEM; ++ ++ plat_data->id = hdmi->id; ++ hdmi->plat_data = plat_data; ++ hdmi->chip_data = plat_data->phy_data; ++ hdmi->is_hdmi_qp = plat_data->is_hdmi_qp; ++ ++ if (hdmi->is_hdmi_qp) { ++ hdmi->workqueue = create_workqueue("hpd_queue"); ++ INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event); ++ } ++ ++ platform_set_drvdata(pdev, hdmi); ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_get_sync(&pdev->dev); ++ + return component_add(&pdev->dev, &dw_hdmi_rockchip_ops); + } + ++static void dw_hdmi_rockchip_shutdown(struct platform_device *pdev) ++{ ++ struct rockchip_hdmi *hdmi = dev_get_drvdata(&pdev->dev); ++ ++ if (!hdmi) ++ return; ++ ++ if (hdmi->is_hdmi_qp) { ++ cancel_delayed_work(&hdmi->work); ++ flush_workqueue(hdmi->workqueue); ++ dw_hdmi_qp_suspend(hdmi->dev, hdmi->hdmi_qp); ++ } else { ++ dw_hdmi_suspend(hdmi->hdmi); ++ } ++ pm_runtime_put_sync(&pdev->dev); ++} ++ + static void dw_hdmi_rockchip_remove(struct platform_device *pdev) + { ++ struct rockchip_hdmi *hdmi = dev_get_drvdata(&pdev->dev); ++ + component_del(&pdev->dev, &dw_hdmi_rockchip_ops); ++ pm_runtime_disable(&pdev->dev); ++ ++ if (hdmi->is_hdmi_qp) ++ destroy_workqueue(hdmi->workqueue); ++} ++ ++static int __maybe_unused dw_hdmi_rockchip_suspend(struct device *dev) ++{ ++ struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); ++ ++ if (hdmi->is_hdmi_qp) ++ dw_hdmi_qp_suspend(dev, hdmi->hdmi_qp); ++ else ++ dw_hdmi_suspend(hdmi->hdmi); ++ ++ pm_runtime_put_sync(dev); ++ ++ return 0; + } + + static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev) + { + struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); ++ u32 val; + +- dw_hdmi_resume(hdmi->hdmi); ++ if (hdmi->is_hdmi_qp) { ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, ++ RK3588_HDMI0_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } else { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, ++ RK3588_HDMI1_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } ++ ++ dw_hdmi_qp_resume(dev, hdmi->hdmi_qp); ++ drm_helper_hpd_irq_event(hdmi->drm_dev); ++ } else { ++ dw_hdmi_resume(hdmi->hdmi); ++ } ++ pm_runtime_get_sync(dev); + + return 0; + } + + static const struct dev_pm_ops dw_hdmi_rockchip_pm = { +- SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume) ++ SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_rockchip_suspend, ++ dw_hdmi_rockchip_resume) + }; + + struct platform_driver dw_hdmi_rockchip_pltfm_driver = { + .probe = dw_hdmi_rockchip_probe, + .remove_new = dw_hdmi_rockchip_remove, ++ .shutdown = dw_hdmi_rockchip_shutdown, + .driver = { + .name = "dwhdmi-rockchip", + .pm = &dw_hdmi_rockchip_pm, +diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h +index 6a46baa0737c..ac4e418c1c4e 100644 +--- a/include/drm/bridge/dw_hdmi.h ++++ b/include/drm/bridge/dw_hdmi.h +@@ -6,12 +6,14 @@ + #ifndef __DW_HDMI__ + #define __DW_HDMI__ + ++#include + #include + + struct drm_display_info; + struct drm_display_mode; + struct drm_encoder; + struct dw_hdmi; ++struct dw_hdmi_qp; + struct platform_device; + + /** +@@ -92,6 +94,13 @@ enum dw_hdmi_phy_type { + DW_HDMI_PHY_VENDOR_PHY = 0xfe, + }; + ++struct dw_hdmi_audio_tmds_n { ++ unsigned long tmds; ++ unsigned int n_32k; ++ unsigned int n_44k1; ++ unsigned int n_48k; ++}; ++ + struct dw_hdmi_mpll_config { + unsigned long mpixelclock; + struct { +@@ -112,6 +121,15 @@ struct dw_hdmi_phy_config { + u16 vlev_ctr; /* voltage level control */ + }; + ++struct dw_hdmi_link_config { ++ bool dsc_mode; ++ bool frl_mode; ++ int frl_lanes; ++ int rate_per_lane; ++ int hcactive; ++ u8 pps_payload[128]; ++}; ++ + struct dw_hdmi_phy_ops { + int (*init)(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *display, +@@ -123,14 +141,52 @@ struct dw_hdmi_phy_ops { + void (*setup_hpd)(struct dw_hdmi *hdmi, void *data); + }; + ++struct dw_hdmi_qp_phy_ops { ++ int (*init)(struct dw_hdmi_qp *hdmi, void *data, ++ struct drm_display_mode *mode); ++ void (*disable)(struct dw_hdmi_qp *hdmi, void *data); ++ enum drm_connector_status (*read_hpd)(struct dw_hdmi_qp *hdmi, ++ void *data); ++ void (*update_hpd)(struct dw_hdmi_qp *hdmi, void *data, ++ bool force, bool disabled, bool rxsense); ++ void (*setup_hpd)(struct dw_hdmi_qp *hdmi, void *data); ++ void (*set_mode)(struct dw_hdmi_qp *dw_hdmi, void *data, ++ u32 mode_mask, bool enable); ++}; ++ ++struct dw_hdmi_property_ops { ++ void (*attach_properties)(struct drm_connector *connector, ++ unsigned int color, int version, ++ void *data); ++ void (*destroy_properties)(struct drm_connector *connector, ++ void *data); ++ int (*set_property)(struct drm_connector *connector, ++ struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 val, ++ void *data); ++ int (*get_property)(struct drm_connector *connector, ++ const struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 *val, ++ void *data); ++}; ++ + struct dw_hdmi_plat_data { + struct regmap *regm; + ++ //[CC:] not in dowstream + unsigned int output_port; + ++ unsigned long input_bus_format; + unsigned long input_bus_encoding; ++ unsigned int max_tmdsclk; ++ int id; + bool use_drm_infoframe; + bool ycbcr_420_allowed; ++ bool unsupported_yuv_input; ++ bool unsupported_deep_color; ++ bool is_hdmi_qp; + + /* + * Private data passed to all the .mode_valid() and .configure_phy() +@@ -139,6 +195,7 @@ struct dw_hdmi_plat_data { + void *priv_data; + + /* Platform-specific mode validation (optional). */ ++ //[CC:] downstream changed "struct dw_hdmi *hdmi" to "struct drm_connector *connector" + enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *info, + const struct drm_display_mode *mode); +@@ -150,18 +207,50 @@ struct dw_hdmi_plat_data { + + /* Vendor PHY support */ + const struct dw_hdmi_phy_ops *phy_ops; ++ const struct dw_hdmi_qp_phy_ops *qp_phy_ops; + const char *phy_name; + void *phy_data; + unsigned int phy_force_vendor; + ++ /* split mode */ ++ bool split_mode; ++ bool first_screen; ++ struct dw_hdmi_qp *left; ++ struct dw_hdmi_qp *right; ++ + /* Synopsys PHY support */ + const struct dw_hdmi_mpll_config *mpll_cfg; ++ const struct dw_hdmi_mpll_config *mpll_cfg_420; + const struct dw_hdmi_curr_ctrl *cur_ctr; + const struct dw_hdmi_phy_config *phy_config; + int (*configure_phy)(struct dw_hdmi *hdmi, void *data, + unsigned long mpixelclock); + + unsigned int disable_cec : 1; ++ ++ //[CC:] 7b29b5f29585 ("drm/rockchip: dw_hdmi: Support HDMI 2.0 YCbCr 4:2:0") ++ unsigned long (*get_input_bus_format)(void *data); ++ unsigned long (*get_output_bus_format)(void *data); ++ unsigned long (*get_enc_in_encoding)(void *data); ++ unsigned long (*get_enc_out_encoding)(void *data); ++ ++ unsigned long (*get_quant_range)(void *data); ++ struct drm_property *(*get_hdr_property)(void *data); ++ struct drm_property_blob *(*get_hdr_blob)(void *data); ++ bool (*get_color_changed)(void *data); ++ int (*get_yuv422_format)(struct drm_connector *connector, ++ struct edid *edid); ++ int (*get_edid_dsc_info)(void *data, struct edid *edid); ++ int (*get_next_hdr_data)(void *data, struct edid *edid, ++ struct drm_connector *connector); ++ struct dw_hdmi_link_config *(*get_link_cfg)(void *data); ++ void (*set_grf_cfg)(void *data); ++ void (*convert_to_split_mode)(struct drm_display_mode *mode); ++ void (*convert_to_origin_mode)(struct drm_display_mode *mode); ++ ++ /* Vendor Property support */ ++ const struct dw_hdmi_property_ops *property_ops; ++ struct drm_connector *connector; + }; + + struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, +@@ -172,6 +261,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, + struct drm_encoder *encoder, + const struct dw_hdmi_plat_data *plat_data); + ++void dw_hdmi_suspend(struct dw_hdmi *hdmi); + void dw_hdmi_resume(struct dw_hdmi *hdmi); + + void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense); +@@ -205,6 +295,17 @@ enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, + void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, + bool force, bool disabled, bool rxsense); + void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data); ++void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi); ++void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val); ++bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi); ++int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi); ++ ++void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi); ++struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, ++ struct drm_encoder *encoder, ++ struct dw_hdmi_plat_data *plat_data); ++void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi); ++void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi); + + bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi); + +-- +Armbian + diff --git a/patch/kernel/rockchip-rk3588-edge/0038-arm64-dts-rockchip-Add-HDMI0-bridge-to-rk3588.patch b/patch/kernel/rockchip-rk3588-edge/0038-arm64-dts-rockchip-Add-HDMI0-bridge-to-rk3588.patch new file mode 100644 index 0000000000..914ce91229 --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0038-arm64-dts-rockchip-Add-HDMI0-bridge-to-rk3588.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Mon, 15 Jan 2024 22:47:41 +0200 +Subject: arm64: dts: rockchip: Add HDMI0 bridge to rk3588 + +Add DT node for the HDMI0 bridge found on RK3588 SoC. + +Signed-off-by: Cristian Ciocaltea +--- + arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 55 ++++++++++ + 1 file changed, 55 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +index 2dfcfe272eb2..c336005577b2 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +@@ -1669,6 +1669,61 @@ i2s9_8ch: i2s@fddfc000 { + status = "disabled"; + }; + ++ hdmi0: hdmi@fde80000 { ++ compatible = "rockchip,rk3588-dw-hdmi"; ++ reg = <0x0 0xfde80000 0x0 0x20000>; ++ interrupts = , ++ , ++ , ++ , ++ ; ++ clocks = <&cru PCLK_HDMITX0>, ++ <&cru CLK_HDMIHDP0>, ++ <&cru CLK_HDMITX0_EARC>, ++ <&cru CLK_HDMITX0_REF>, ++ <&cru MCLK_I2S5_8CH_TX>, ++ <&cru DCLK_VOP0>, ++ <&cru DCLK_VOP1>, ++ <&cru DCLK_VOP2>, ++ <&cru DCLK_VOP3>, ++ <&cru HCLK_VO1>; ++ clock-names = "pclk", ++ "hpd", ++ "earc", ++ "hdmitx_ref", ++ "aud", ++ "dclk_vp0", ++ "dclk_vp1", ++ "dclk_vp2", ++ "dclk_vp3", ++ "hclk_vo1"; ++ resets = <&cru SRST_HDMITX0_REF>, <&cru SRST_HDMIHDP0>; ++ reset-names = "ref", "hdp"; ++ power-domains = <&power RK3588_PD_VO1>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hdmim0_tx0_cec &hdmim0_tx0_hpd ++ &hdmim0_tx0_scl &hdmim0_tx0_sda>; ++ reg-io-width = <4>; ++ rockchip,grf = <&sys_grf>; ++ rockchip,vo1_grf = <&vo1_grf>; ++ phys = <&hdptxphy_hdmi0>; ++ phy-names = "hdmi"; ++ status = "disabled"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hdmi0_in: port@0 { ++ reg = <0>; ++ }; ++ ++ hdmi0_out: port@1 { ++ reg = <1>; ++ }; ++ }; ++ }; ++ + qos_gpu_m0: qos@fdf35000 { + compatible = "rockchip,rk3588-qos", "syscon"; + reg = <0x0 0xfdf35000 0x0 0x20>; +-- +Armbian + diff --git a/patch/kernel/rockchip-rk3588-edge/0039-arm64-dts-rockchip-Enable-HDMI0-PHY-clk-provider-on-.patch b/patch/kernel/rockchip-rk3588-edge/0039-arm64-dts-rockchip-Enable-HDMI0-PHY-clk-provider-on-.patch new file mode 100644 index 0000000000..8532a362be --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0039-arm64-dts-rockchip-Enable-HDMI0-PHY-clk-provider-on-.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Tue, 16 Jan 2024 03:13:38 +0200 +Subject: arm64: dts: rockchip: Enable HDMI0 PHY clk provider on rk3588 + +The HDMI0 PHY can be used as a clock provider on RK3588, hence add the +missing #clock-cells property. +--- + arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +index c336005577b2..762a095648b1 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +@@ -3004,6 +3004,7 @@ hdptxphy_hdmi0: phy@fed60000 { + reg = <0x0 0xfed60000 0x0 0x2000>; + clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>, <&cru PCLK_HDPTX0>; + clock-names = "ref", "apb"; ++ #clock-cells = <0>; + #phy-cells = <0>; + resets = <&cru SRST_HDPTX0>, <&cru SRST_P_HDPTX0>, + <&cru SRST_HDPTX0_INIT>, <&cru SRST_HDPTX0_CMN>, +-- +Armbian + diff --git a/patch/kernel/rockchip-rk3588-edge/0032-Add-HDMI-and-VOP2-to-Rock-5B.patch b/patch/kernel/rockchip-rk3588-edge/0040-arm64-dts-rockchip-Enable-HDMI0-on-rock-5b.patch similarity index 53% rename from patch/kernel/rockchip-rk3588-edge/0032-Add-HDMI-and-VOP2-to-Rock-5B.patch rename to patch/kernel/rockchip-rk3588-edge/0040-arm64-dts-rockchip-Enable-HDMI0-on-rock-5b.patch index 9f45c70222..f320bf1be6 100644 --- a/patch/kernel/rockchip-rk3588-edge/0032-Add-HDMI-and-VOP2-to-Rock-5B.patch +++ b/patch/kernel/rockchip-rk3588-edge/0040-arm64-dts-rockchip-Enable-HDMI0-on-rock-5b.patch @@ -1,14 +1,17 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Muhammed Efe Cetin -Date: Tue, 27 Feb 2024 16:08:14 +0300 -Subject: Add HDMI and VOP2 to Rock 5B +From: Cristian Ciocaltea +Date: Mon, 15 Jan 2024 22:51:17 +0200 +Subject: arm64: dts: rockchip: Enable HDMI0 on rock-5b +Add the necessary DT changes to enable HDMI0 on Rock 5B. + +Signed-off-by: Cristian Ciocaltea --- - arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts | 48 ++++++++++ - 1 file changed, 48 insertions(+) + arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts | 30 ++++++++++ + 1 file changed, 30 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts -index f4df3e683e76..0ff6aa11fda8 100644 +index f4df3e683e76..98192a35920c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts @@ -4,6 +4,7 @@ @@ -19,39 +22,11 @@ index f4df3e683e76..0ff6aa11fda8 100644 #include "rk3588.dtsi" / { -@@ -58,6 +59,17 @@ fan: pwm-fan { - #cooling-cells = <2>; - }; - -+ hdmi-con { -+ compatible = "hdmi-connector"; -+ type = "a"; -+ -+ port { -+ hdmi_con_in: endpoint { -+ remote-endpoint = <&hdmi0_out_con>; -+ }; -+ }; -+ }; -+ - vcc3v3_pcie2x1l0: vcc3v3-pcie2x1l0-regulator { - compatible = "regulator-fixed"; - enable-active-high; -@@ -802,3 +814,39 @@ &usb_host1_xhci { - &usb_host2_xhci { - status = "okay"; +@@ -181,6 +182,20 @@ &cpu_l3 { + mem-supply = <&vdd_cpu_lit_mem_s0>; }; -+ + +&hdmi0 { -+ enable-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>; -+ status = "okay"; -+}; -+ -+&hdptxphy_hdmi0 { -+ status = "okay"; -+}; -+ -+&vop_mmu { + status = "okay"; +}; + @@ -61,10 +36,20 @@ index f4df3e683e76..0ff6aa11fda8 100644 + }; +}; + -+&hdmi0_out { -+ hdmi0_out_con: endpoint { -+ remote-endpoint = <&hdmi_con_in>; -+ }; ++&hdptxphy_hdmi0 { ++ status = "okay"; ++}; ++ + &i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0m2_xfer>; +@@ -802,3 +817,18 @@ &usb_host1_xhci { + &usb_host2_xhci { + status = "okay"; + }; ++ ++&vop_mmu { ++ status = "okay"; +}; + +&vop { diff --git a/patch/kernel/rockchip-rk3588-edge/board-khadas-edge2-add-nodes.patch b/patch/kernel/rockchip-rk3588-edge/board-khadas-edge2-add-nodes.patch index 765ce87663..2903140474 100644 --- a/patch/kernel/rockchip-rk3588-edge/board-khadas-edge2-add-nodes.patch +++ b/patch/kernel/rockchip-rk3588-edge/board-khadas-edge2-add-nodes.patch @@ -1245,11 +1245,11 @@ Date: Mon, 19 Feb 2024 23:32:11 +0300 Subject: arm64: dts: rockchip: Add HDMI & VOP2 to Khadas Edge 2 --- - arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts | 50 ++++++++++ - 1 file changed, 50 insertions(+) + arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts | 32 ++++++++++ + 1 file changed, 32 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts b/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts -index b95a36b717f1..0e0afa68f65c 100644 +index b95a36b717f1..0d0d7feaea5a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts @@ -7,6 +7,7 @@ @@ -1260,25 +1260,7 @@ index b95a36b717f1..0e0afa68f65c 100644 #include "rk3588s.dtsi" / { -@@ -77,6 +78,17 @@ blue_led: led-2 { - }; - }; - -+ hdmi-con { -+ compatible = "hdmi-connector"; -+ type = "a"; -+ -+ port { -+ hdmi_con_in: endpoint { -+ remote-endpoint = <&hdmi0_out_con>; -+ }; -+ }; -+ }; -+ - bluetooth-rfkill { - compatible = "rfkill-gpio"; - label = "rfkill-bluetooth"; -@@ -821,6 +833,7 @@ usbdp_phy0_dp_altmode_mux: endpoint@1 { +@@ -821,6 +822,7 @@ usbdp_phy0_dp_altmode_mux: endpoint@1 { }; &usb_host0_xhci { @@ -1286,13 +1268,12 @@ index b95a36b717f1..0e0afa68f65c 100644 usb-role-switch; status = "okay"; -@@ -845,3 +858,40 @@ &usb_host1_ohci { +@@ -845,3 +847,33 @@ &usb_host1_ohci { &usb_host2_xhci { status = "okay"; }; + +&hdmi0 { -+ enable-gpios = <&gpio4 RK_PB1 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + @@ -1311,12 +1292,6 @@ index b95a36b717f1..0e0afa68f65c 100644 + }; +}; + -+&hdmi0_out { -+ hdmi0_out_con: endpoint { -+ remote-endpoint = <&hdmi_con_in>; -+ }; -+}; -+ +&vop { + status = "okay"; +}; @@ -1340,10 +1315,10 @@ Subject: arm64: dts: rockchip: Add AP6275P wireless support to Khadas Edge 2 1 file changed, 17 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts b/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts -index 0e0afa68f65c..931ef1c49cb7 100644 +index 0d0d7feaea5a..fe6321eb5f6d 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts -@@ -372,6 +372,23 @@ &pcie2x1l2 { +@@ -361,6 +361,23 @@ &pcie2x1l2 { reset-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_HIGH>; vpcie3v3-supply = <&vcc3v3_pcie_wl>; status = "okay"; @@ -1380,10 +1355,10 @@ Subject: arm64: dts: rockchip: Add cpufreq support to Khadas Edge 2 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts b/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts -index 931ef1c49cb7..8b40b5af6722 100644 +index fe6321eb5f6d..61a5b98443e9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts -@@ -172,34 +172,42 @@ vdd_3v3_sd: vdd-3v3-sd-regulator { +@@ -161,34 +161,42 @@ vdd_3v3_sd: vdd-3v3-sd-regulator { &cpu_b0 { cpu-supply = <&vdd_cpu_big0_s0>; @@ -1426,7 +1401,7 @@ index 931ef1c49cb7..8b40b5af6722 100644 }; &combphy0_ps { -@@ -215,7 +223,7 @@ &i2c0 { +@@ -204,7 +212,7 @@ &i2c0 { pinctrl-0 = <&i2c0m2_xfer>; status = "okay"; @@ -1435,7 +1410,7 @@ index 931ef1c49cb7..8b40b5af6722 100644 compatible = "rockchip,rk8602"; reg = <0x42>; fcs,suspend-voltage-selector = <1>; -@@ -232,7 +240,7 @@ regulator-state-mem { +@@ -221,7 +229,7 @@ regulator-state-mem { }; }; diff --git a/patch/kernel/rockchip-rk3588-edge/board-khadas-edge2-mcu.patch b/patch/kernel/rockchip-rk3588-edge/board-khadas-edge2-mcu.patch index d5d9049f33..a3995e4e3c 100644 --- a/patch/kernel/rockchip-rk3588-edge/board-khadas-edge2-mcu.patch +++ b/patch/kernel/rockchip-rk3588-edge/board-khadas-edge2-mcu.patch @@ -1,11 +1,11 @@ -From a7345c49995eb3fefdccc83299a25ee20e995d0a Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Wed, 6 Mar 2024 00:09:25 +0300 -Subject: [PATCH 1/5] mfd: khadas-mcu: add Edge2 registers +Subject: mfd: khadas-mcu: add Edge2 registers --- - drivers/mfd/khadas-mcu.c | 8 ++++++-- - include/linux/mfd/khadas-mcu.h | 24 ++++++++++++++++++++++++ + drivers/mfd/khadas-mcu.c | 8 +++- + include/linux/mfd/khadas-mcu.h | 24 ++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/khadas-mcu.c b/drivers/mfd/khadas-mcu.c @@ -110,16 +110,15 @@ index a99ba2ed0e4e..63bc9bf76661 100644 /** -- -2.43.1 +Armbian - -From 1abf1f3350b22f9a6680d00854bbcd0aab4cfbd5 Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Wed, 6 Mar 2024 00:09:58 +0300 -Subject: [PATCH 2/5] mfd: khadas-mcu: drop unused code +Subject: mfd: khadas-mcu: drop unused code --- - drivers/mfd/khadas-mcu.c | 11 ----------- + drivers/mfd/khadas-mcu.c | 11 ---------- 1 file changed, 11 deletions(-) diff --git a/drivers/mfd/khadas-mcu.c b/drivers/mfd/khadas-mcu.c @@ -152,16 +151,15 @@ index a1a63fb70aac..805fb9eb4a2f 100644 return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, khadas_mcu_fan_cells, -- -2.43.1 +Armbian - -From dccc32412d58027f9b8661379faf5e0027a3516e Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Wed, 6 Mar 2024 00:13:10 +0300 -Subject: [PATCH 3/5] thermal: khadas_mcu_fan: add support for Khadas Edge 2 +Subject: thermal: khadas_mcu_fan: add support for Khadas Edge 2 --- - drivers/thermal/khadas_mcu_fan.c | 77 ++++++++++++++++++++++++++++++-- + drivers/thermal/khadas_mcu_fan.c | 77 +++++++++- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c @@ -293,13 +291,12 @@ index d35e5313bea4..e495e562c346 100644 dev->parent->of_node, "khadas-mcu-fan", ctx, &khadas_mcu_fan_cooling_ops); -- -2.43.1 +Armbian - -From 1648a7e9bc3357b04a316a59b7f3fcef85bf5bb9 Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Wed, 6 Mar 2024 00:14:58 +0300 -Subject: [PATCH 4/5] dt-bindings: mfd: khadas-mcu: add cooling-levels property +Subject: dt-bindings: mfd: khadas-mcu: add cooling-levels property --- Documentation/devicetree/bindings/mfd/khadas,mcu.yaml | 7 ++++++- @@ -331,23 +328,22 @@ index 084960fd5a1f..cf46b690010f 100644 maxItems: 1 -- -2.43.1 +Armbian - -From 20f5c5601994c25df607625dd9f882eb43466f56 Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Wed, 6 Mar 2024 00:17:58 +0300 -Subject: [PATCH 5/5] arm64: dts: rockchip: Add MCU to Khadas Edge 2 +Subject: arm64: dts: rockchip: Add MCU to Khadas Edge 2 --- arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts b/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts -index a0f335efa2e1..168f32ea8a57 100644 +index 61a5b98443e9..25b515eb35f4 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-khadas-edge2.dts -@@ -318,6 +318,13 @@ hym8563: rtc@51 { +@@ -307,6 +307,13 @@ hym8563: rtc@51 { clock-output-names = "hym8563"; wakeup-source; }; @@ -362,5 +358,5 @@ index a0f335efa2e1..168f32ea8a57 100644 &pinctrl { -- -2.43.1 +Armbian diff --git a/patch/kernel/rockchip-rk3588-edge/dt/rk3588-hinlink-h88k.dts b/patch/kernel/rockchip-rk3588-edge/dt/rk3588-hinlink-h88k.dts index f7776d1e30..b403216766 100644 --- a/patch/kernel/rockchip-rk3588-edge/dt/rk3588-hinlink-h88k.dts +++ b/patch/kernel/rockchip-rk3588-edge/dt/rk3588-hinlink-h88k.dts @@ -63,17 +63,6 @@ stdout-path = "serial2:1500000n8"; }; - hdmi-con { - compatible = "hdmi-connector"; - type = "a"; - - port { - hdmi_con_in: endpoint { - remote-endpoint = <&hdmi0_out_con>; - }; - }; - }; - leds { compatible = "gpio-leds"; pinctrl-names = "default"; @@ -212,7 +201,6 @@ }; &hdmi0 { - enable-gpios = <&gpio4 RK_PB1 GPIO_ACTIVE_HIGH>; status = "okay"; }; @@ -227,12 +215,6 @@ }; }; -&hdmi0_out { - hdmi0_out_con: endpoint { - remote-endpoint = <&hdmi_con_in>; - }; -}; - &i2c0 { pinctrl-names = "default"; pinctrl-0 = <&i2c0m2_xfer>; diff --git a/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-nanopi-r6s.dts b/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-nanopi-r6s.dts index 6667be1880..040fdd6e71 100644 --- a/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-nanopi-r6s.dts +++ b/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-nanopi-r6s.dts @@ -86,17 +86,6 @@ }; }; - hdmi-con { - compatible = "hdmi-connector"; - type = "a"; - - port { - hdmi_con_in: endpoint { - remote-endpoint = <&hdmi0_out_con>; - }; - }; - }; - vcc5v0_sys: vcc5v0-sys-regulator { compatible = "regulator-fixed"; regulator-name = "vcc5v0_sys"; @@ -839,7 +828,6 @@ }; &hdmi0 { - enable-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>; status = "okay"; }; @@ -858,12 +846,6 @@ }; }; -&hdmi0_out { - hdmi0_out_con: endpoint { - remote-endpoint = <&hdmi_con_in>; - }; -}; - &vop { status = "okay"; };