diff --git a/patch/kernel/archive/meson-6.7/generic-0001-m8-m8b-m8m2-Support-HDMI.patch b/patch/kernel/archive/meson-6.7/generic-0001-m8-m8b-m8m2-Support-HDMI.patch new file mode 100644 index 0000000000..3408e53f8d --- /dev/null +++ b/patch/kernel/archive/meson-6.7/generic-0001-m8-m8b-m8m2-Support-HDMI.patch @@ -0,0 +1,4399 @@ +From 4367c45014e0ada25b438017df26140eeb795260 Mon Sep 17 00:00:00 2001 +From: Martin Blumenstingl +Date: Sat, 18 Nov 2023 01:22:02 +0800 +Subject: [PATCH 1/3] meson8/meson8b/meson8m2: Support HDMI + +The following codes are come from https://github.com/xdarklight/linux/commits/meson-mx-integration-5.18-20220516. + +Special thank to Martin Blumenstingl. + +--- + .../bindings/display/amlogic,meson-vpu.yaml | 16 + + .../phy/amlogic,meson-cvbs-dac-phy.yaml | 81 + + arch/arm/boot/dts/amlogic/meson.dtsi | 13 + + arch/arm/boot/dts/amlogic/meson8.dtsi | 168 +- + arch/arm/boot/dts/amlogic/meson8b.dtsi | 171 +- + arch/arm/boot/dts/amlogic/meson8m2.dtsi | 4 + + drivers/gpu/drm/meson/Kconfig | 9 + + drivers/gpu/drm/meson/Makefile | 1 + + drivers/gpu/drm/meson/meson_drv.c | 313 +++- + drivers/gpu/drm/meson/meson_drv.h | 49 +- + drivers/gpu/drm/meson/meson_encoder_cvbs.c | 61 +- + drivers/gpu/drm/meson/meson_encoder_hdmi.c | 69 +- + drivers/gpu/drm/meson/meson_plane.c | 37 +- + drivers/gpu/drm/meson/meson_transwitch_hdmi.c | 1579 +++++++++++++++++ + drivers/gpu/drm/meson/meson_transwitch_hdmi.h | 536 ++++++ + drivers/gpu/drm/meson/meson_vclk.c | 146 ++ + drivers/gpu/drm/meson/meson_venc.c | 44 +- + drivers/gpu/drm/meson/meson_viu.c | 18 +- + drivers/phy/amlogic/Kconfig | 10 + + drivers/phy/amlogic/Makefile | 1 + + drivers/phy/amlogic/phy-meson-cvbs-dac.c | 375 ++++ + 21 files changed, 3576 insertions(+), 125 deletions(-) + create mode 100644 Documentation/devicetree/bindings/phy/amlogic,meson-cvbs-dac-phy.yaml + create mode 100644 drivers/gpu/drm/meson/meson_transwitch_hdmi.c + create mode 100644 drivers/gpu/drm/meson/meson_transwitch_hdmi.h + create mode 100644 drivers/phy/amlogic/phy-meson-cvbs-dac.c + +diff --git a/Documentation/devicetree/bindings/display/amlogic,meson-vpu.yaml b/Documentation/devicetree/bindings/display/amlogic,meson-vpu.yaml +index cb0a90f0..96c32747 100644 +--- a/Documentation/devicetree/bindings/display/amlogic,meson-vpu.yaml ++++ b/Documentation/devicetree/bindings/display/amlogic,meson-vpu.yaml +@@ -66,8 +66,12 @@ properties: + - const: amlogic,meson-gx-vpu + - enum: + - amlogic,meson-g12a-vpu # G12A (S905X2, S905Y2, S905D2) ++ - amlogic,meson8-vpu ++ - amlogic,meson8b-vpu ++ - amlogic,meson8m2-vpu + + reg: ++ minItems: 1 + maxItems: 2 + + reg-names: +@@ -82,6 +86,15 @@ properties: + description: should point to a canvas provider node + $ref: /schemas/types.yaml#/definitions/phandle + ++ phys: ++ maxItems: 1 ++ description: ++ PHY specifier for the CVBS DAC ++ ++ phy-names: ++ items: ++ - const: cvbs-dac ++ + power-domains: + maxItems: 1 + description: phandle to the associated power domain +@@ -130,6 +143,9 @@ examples: + #size-cells = <0>; + amlogic,canvas = <&canvas>; + ++ phys = <&cvbs_dac_phy>; ++ phy-names = "cvbs-dac"; ++ + /* CVBS VDAC output port */ + port@0 { + reg = <0>; +diff --git a/Documentation/devicetree/bindings/phy/amlogic,meson-cvbs-dac-phy.yaml b/Documentation/devicetree/bindings/phy/amlogic,meson-cvbs-dac-phy.yaml +new file mode 100644 +index 00000000..d73cb12c +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/amlogic,meson-cvbs-dac-phy.yaml +@@ -0,0 +1,81 @@ ++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: "http://devicetree.org/schemas/phy/amlogic,meson-cvbs-dac-phy.yaml#" ++$schema: "http://devicetree.org/meta-schemas/core.yaml#" ++ ++title: Amlogic Meson Composite Video Baseband Signal DAC ++ ++maintainers: ++ - Martin Blumenstingl ++ ++description: |+ ++ The CVBS DAC node should be the child of a syscon node with the ++ required property: ++ ++ compatible = "amlogic,meson-hhi-sysctrl", "simple-mfd", "syscon" ++ ++ Refer to the bindings described in ++ Documentation/devicetree/bindings/mfd/syscon.yaml ++ ++properties: ++ $nodename: ++ pattern: "^video-dac@[0-9a-f]+$" ++ ++ compatible: ++ oneOf: ++ - items: ++ - enum: ++ - amlogic,meson8-cvbs-dac ++ - amlogic,meson-gxbb-cvbs-dac ++ - amlogic,meson-gxl-cvbs-dac ++ - amlogic,meson-g12a-cvbs-dac ++ - const: amlogic,meson-cvbs-dac ++ - const: amlogic,meson-cvbs-dac ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ minItems: 1 ++ ++ nvmem-cells: ++ minItems: 1 ++ ++ nvmem-cell-names: ++ items: ++ - const: cvbs_trimming ++ ++ "#phy-cells": ++ const: 0 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - "#phy-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ video-dac@2f4 { ++ compatible = "amlogic,meson8-cvbs-dac", "amlogic,meson-cvbs-dac"; ++ reg = <0x2f4 0x8>; ++ ++ #phy-cells = <0>; ++ ++ clocks = <&vdac_clock>; ++ ++ nvmem-cells = <&cvbs_trimming>; ++ nvmem-cell-names = "cvbs_trimming"; ++ }; ++ - | ++ video-dac@2ec { ++ compatible = "amlogic,meson-g12a-cvbs-dac", "amlogic,meson-cvbs-dac"; ++ reg = <0x2ec 0x8>; ++ ++ #phy-cells = <0>; ++ ++ clocks = <&vdac_clock>; ++ }; +diff --git a/arch/arm/boot/dts/amlogic/meson.dtsi b/arch/arm/boot/dts/amlogic/meson.dtsi +index 8e3860d5..9a56cdf7 100644 +--- a/arch/arm/boot/dts/amlogic/meson.dtsi ++++ b/arch/arm/boot/dts/amlogic/meson.dtsi +@@ -35,6 +35,19 @@ hhi: system-controller@4000 { + "simple-mfd", + "syscon"; + reg = <0x4000 0x400>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x0 0x4000 0x400>; ++ ++ ++ cvbs_dac: video-dac@2f4 { ++ compatible = "amlogic,meson-cvbs-dac"; ++ reg = <0x2f4 0x8>; ++ ++ #phy-cells = <0>; ++ ++ status = "disabled"; ++ }; + }; + + aiu: audio-controller@5400 { +diff --git a/arch/arm/boot/dts/amlogic/meson8.dtsi b/arch/arm/boot/dts/amlogic/meson8.dtsi +index 59932fbf..6c27d520 100644 +--- a/arch/arm/boot/dts/amlogic/meson8.dtsi ++++ b/arch/arm/boot/dts/amlogic/meson8.dtsi +@@ -314,6 +314,113 @@ mali: gpu@c0000 { + operating-points-v2 = <&gpu_opp_table>; + #cooling-cells = <2>; /* min followed by max */ + }; ++ ++ hdmi_tx: hdmi-tx@42000 { ++ compatible = "amlogic,meson8-hdmi-tx"; ++ reg = <0x42000 0xc>; ++ interrupts = ; ++ phys = <&hdmi_tx_phy>; ++ phy-names = "hdmi"; ++ clocks = <&clkc CLKID_HDMI_PCLK>, ++ <&clkc CLKID_HDMI_SYS>; ++ clock-names = "pclk", "sys"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ #sound-dai-cells = <1>; ++ sound-name-prefix = "HDMITX"; ++ ++ status = "disabled"; ++ ++ /* VPU VENC Input */ ++ hdmi_tx_venc_port: port@0 { ++ reg = <0>; ++ ++ hdmi_tx_in: endpoint { ++ remote-endpoint = <&hdmi_tx_out>; ++ }; ++ }; ++ ++ /* TMDS Output */ ++ hdmi_tx_tmds_port: port@1 { ++ reg = <1>; ++ }; ++ }; ++ ++ vpu: vpu@100000 { ++ compatible = "amlogic,meson8-vpu"; ++ ++ reg = <0x100000 0x10000>; ++ reg-names = "vpu"; ++ ++ interrupts = ; ++ ++ amlogic,canvas = <&canvas>; ++ ++ /* ++ * The VCLK{,2}_IN path always needs to derived from ++ * the CLKID_VID_PLL_FINAL_DIV so other clocks like ++ * MPLL1 are not used (MPLL1 is reserved for audio ++ * purposes). ++ */ ++ assigned-clocks = <&clkc CLKID_VCLK_IN_SEL>, ++ <&clkc CLKID_VCLK2_IN_SEL>; ++ assigned-clock-parents = <&clkc CLKID_VID_PLL_FINAL_DIV>, ++ <&clkc CLKID_VID_PLL_FINAL_DIV>; ++ ++ clocks = <&clkc CLKID_VPU_INTR>, ++ <&clkc CLKID_HDMI_INTR_SYNC>, ++ <&clkc CLKID_GCLK_VENCI_INT>, ++ <&clkc CLKID_HDMI_PLL_HDMI_OUT>, ++ <&clkc CLKID_HDMI_TX_PIXEL>, ++ <&clkc CLKID_CTS_ENCP>, ++ <&clkc CLKID_CTS_ENCI>, ++ <&clkc CLKID_CTS_ENCT>, ++ <&clkc CLKID_CTS_ENCL>, ++ <&clkc CLKID_CTS_VDAC0>; ++ clock-names = "vpu_intr", ++ "hdmi_intr_sync", ++ "venci_int", ++ "tmds", ++ "hdmi_tx_pixel", ++ "cts_encp", ++ "cts_enci", ++ "cts_enct", ++ "cts_encl", ++ "cts_vdac0"; ++ ++ resets = <&clkc CLKC_RESET_VID_DIVIDER_CNTL_RESET_N_PRE>, ++ <&clkc CLKC_RESET_VID_DIVIDER_CNTL_RESET_N_POST>, ++ <&clkc CLKC_RESET_VID_DIVIDER_CNTL_SOFT_RESET_PRE>, ++ <&clkc CLKC_RESET_VID_DIVIDER_CNTL_SOFT_RESET_POST>; ++ reset-names = "vid_pll_pre", ++ "vid_pll_post", ++ "vid_pll_soft_pre", ++ "vid_pll_soft_post"; ++ ++ phys = <&cvbs_dac>; ++ phy-names = "cvbs-dac"; ++ ++ power-domains = <&pwrc PWRC_MESON8_VPU_ID>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* CVBS VDAC output port */ ++ cvbs_vdac_port: port@0 { ++ reg = <0>; ++ }; ++ ++ /* HDMI-TX output port */ ++ hdmi_tx_port: port@1 { ++ reg = <1>; ++ ++ hdmi_tx_out: endpoint { ++ remote-endpoint = <&hdmi_tx_in>; ++ }; ++ }; ++ }; + }; + }; /* end of / */ + +@@ -363,6 +470,14 @@ gpio_ao: ao-bank@14 { + gpio-ranges = <&pinctrl_aobus 0 0 16>; + }; + ++ hdmi_cec_ao_pins: hdmi-cec-ao { ++ mux { ++ groups = "hdmi_cec_ao"; ++ function = "hdmi_cec_ao"; ++ bias-pull-up; ++ }; ++ }; ++ + i2s_am_clk_pins: i2s-am-clk-out { + mux { + groups = "i2s_am_clk_out_ao"; +@@ -427,6 +542,15 @@ mux { + }; + }; + }; ++ ++ cec_AO: cec@100 { ++ compatible = "amlogic,meson-gx-ao-cec"; // FIXME ++ reg = <0x100 0x14>; ++ interrupts = ; ++ // TODO: 32768HZ clock ++ hdmi-phandle = <&hdmi_tx>; ++ status = "disabled"; ++ }; + }; + + &ao_arc_rproc { +@@ -479,6 +603,22 @@ gpio: banks@80b0 { + gpio-ranges = <&pinctrl_cbus 0 0 120>; + }; + ++ hdmi_hpd_pins: hdmi-hpd { ++ mux { ++ groups = "hdmi_hpd"; ++ function = "hdmi"; ++ bias-disable; ++ }; ++ }; ++ ++ hdmi_i2c_pins: hdmi-i2c { ++ mux { ++ groups = "hdmi_sda", "hdmi_scl"; ++ function = "hdmi"; ++ bias-disable; ++ }; ++ }; ++ + sd_a_pins: sd-a { + mux { + groups = "sd_d0_a", "sd_d1_a", "sd_d2_a", +@@ -601,6 +741,17 @@ smp-sram@1ff80 { + }; + }; + ++&cvbs_dac { ++ compatible = "amlogic,meson8-cvbs-dac", "amlogic,meson-cvbs-dac"; ++ ++ clocks = <&clkc CLKID_CTS_VDAC0>; ++ ++ nvmem-cells = <&cvbs_trimming>; ++ nvmem-cell-names = "cvbs_trimming"; ++ ++ status = "okay"; ++}; ++ + &efuse { + compatible = "amlogic,meson8-efuse"; + clocks = <&clkc CLKID_EFUSE>; +@@ -610,6 +761,10 @@ temperature_calib: calib@1f4 { + /* only the upper two bytes are relevant */ + reg = <0x1f4 0x4>; + }; ++ ++ cvbs_trimming: calib@1f8 { ++ reg = <0x1f8 0x2>; ++ }; + }; + + ðmac { +@@ -625,16 +780,18 @@ &gpio_intc { + }; + + &hhi { +- clkc: clock-controller { ++ clkc: clock-controller@0 { + compatible = "amlogic,meson8-clkc"; ++ reg = <0x0 0x39c>; + clocks = <&xtal>, <&ddr_clkc DDR_CLKID_DDR_PLL>; + clock-names = "xtal", "ddr_pll"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + +- pwrc: power-controller { ++ pwrc: power-controller@100 { + compatible = "amlogic,meson8-pwrc"; ++ reg = <0x100 0x10>; + #power-domain-cells = <1>; + amlogic,ao-sysctrl = <&pmu>; + clocks = <&clkc CLKID_VPU>; +@@ -642,6 +799,13 @@ pwrc: power-controller { + assigned-clocks = <&clkc CLKID_VPU>; + assigned-clock-rates = <364285714>; + }; ++ ++ hdmi_tx_phy: hdmi-phy@3a0 { ++ compatible = "amlogic,meson8-hdmi-tx-phy"; ++ clocks = <&clkc CLKID_HDMI_PLL_HDMI_OUT>; ++ reg = <0x3a0 0xc>; ++ #phy-cells = <0>; ++ }; + }; + + &hwrng { +diff --git a/arch/arm/boot/dts/amlogic/meson8b.dtsi b/arch/arm/boot/dts/amlogic/meson8b.dtsi +index 5198f517..da9216ee 100644 +--- a/arch/arm/boot/dts/amlogic/meson8b.dtsi ++++ b/arch/arm/boot/dts/amlogic/meson8b.dtsi +@@ -276,6 +276,116 @@ mali: gpu@c0000 { + operating-points-v2 = <&gpu_opp_table>; + #cooling-cells = <2>; /* min followed by max */ + }; ++ ++ hdmi_tx: hdmi-tx@42000 { ++ compatible = "amlogic,meson8b-hdmi-tx"; ++ reg = <0x42000 0xc>; ++ interrupts = ; ++ phys = <&hdmi_tx_phy>; ++ phy-names = "hdmi"; ++ clocks = <&clkc CLKID_HDMI_PCLK>, ++ <&clkc CLKID_HDMI_SYS>; ++ clock-names = "pclk", "sys"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ #sound-dai-cells = <1>; ++ sound-name-prefix = "HDMITX"; ++ ++ status = "disabled"; ++ ++ /* VPU VENC Input */ ++ hdmi_tx_venc_port: port@0 { ++ reg = <0>; ++ ++ hdmi_tx_in: endpoint { ++ remote-endpoint = <&hdmi_tx_out>; ++ }; ++ }; ++ ++ /* TMDS Output */ ++ hdmi_tx_tmds_port: port@1 { ++ reg = <1>; ++ }; ++ }; ++ ++ vpu: vpu@100000 { ++ compatible = "amlogic,meson8b-vpu"; ++ ++ reg = <0x100000 0x10000>; ++ reg-names = "vpu"; ++ ++ interrupts = ; ++ ++ amlogic,canvas = <&canvas>; ++ ++ /* ++ * The VCLK{,2}_IN path always needs to derived from ++ * the CLKID_VID_PLL_FINAL_DIV so other clocks like ++ * MPLL1 are not used (MPLL1 is reserved for audio ++ * purposes). ++ */ ++ assigned-clocks = <&clkc CLKID_VCLK_IN_SEL>, ++ <&clkc CLKID_VCLK2_IN_SEL>; ++ assigned-clock-parents = <&clkc CLKID_VID_PLL_FINAL_DIV>, ++ <&clkc CLKID_VID_PLL_FINAL_DIV>; ++ ++ clocks = <&clkc CLKID_VPU_INTR>, ++ <&clkc CLKID_HDMI_INTR_SYNC>, ++ <&clkc CLKID_GCLK_VENCI_INT>, ++ <&clkc CLKID_HDMI_PLL_HDMI_OUT>, ++ <&clkc CLKID_HDMI_TX_PIXEL>, ++ <&clkc CLKID_CTS_ENCP>, ++ <&clkc CLKID_CTS_ENCI>, ++ <&clkc CLKID_CTS_ENCT>, ++ <&clkc CLKID_CTS_ENCL>, ++ <&clkc CLKID_CTS_VDAC0>; ++ clock-names = "vpu_intr", ++ "hdmi_intr_sync", ++ "venci_int", ++ "tmds", ++ "hdmi_tx_pixel", ++ "cts_encp", ++ "cts_enci", ++ "cts_enct", ++ "cts_encl", ++ "cts_vdac0"; ++ ++ resets = <&clkc CLKC_RESET_VID_DIVIDER_CNTL_RESET_N_PRE>, ++ <&clkc CLKC_RESET_VID_DIVIDER_CNTL_RESET_N_POST>, ++ <&clkc CLKC_RESET_VID_DIVIDER_CNTL_SOFT_RESET_PRE>, ++ <&clkc CLKC_RESET_VID_DIVIDER_CNTL_SOFT_RESET_POST>; ++ reset-names = "vid_pll_pre", ++ "vid_pll_post", ++ "vid_pll_soft_pre", ++ "vid_pll_soft_post"; ++ ++ phys = <&cvbs_dac>; ++ phy-names = "cvbs-dac"; ++ ++ power-domains = <&pwrc PWRC_MESON8_VPU_ID>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ #sound-dai-cells = <0>; ++ sound-name-prefix = "HDMITX"; ++ ++ /* CVBS VDAC output port */ ++ cvbs_vdac_port: port@0 { ++ reg = <0>; ++ }; ++ ++ /* HDMI-TX output port */ ++ hdmi_tx_port: port@1 { ++ reg = <1>; ++ ++ hdmi_tx_out: endpoint { ++ remote-endpoint = <&hdmi_tx_in>; ++ }; ++ }; ++ }; + }; + }; /* end of / */ + +@@ -325,6 +435,14 @@ gpio_ao: ao-bank@14 { + gpio-ranges = <&pinctrl_aobus 0 0 16>; + }; + ++ hdmi_cec_ao_pins: hdmi-cec-ao { ++ mux { ++ groups = "hdmi_cec_1"; ++ function = "hdmi_cec"; ++ bias-pull-up; ++ }; ++ }; ++ + i2s_am_clk_pins: i2s-am-clk-out { + mux { + groups = "i2s_am_clk_out"; +@@ -381,6 +499,15 @@ mux { + }; + }; + }; ++ ++ cec_AO: cec@100 { ++ compatible = "amlogic,meson-gx-ao-cec"; // FIXME ++ reg = <0x100 0x14>; ++ interrupts = ; ++ // TODO: 32768HZ clock ++ hdmi-phandle = <&hdmi_tx>; ++ status = "disabled"; ++ }; + }; + + &ao_arc_rproc { +@@ -471,6 +598,22 @@ mux { + }; + }; + ++ hdmi_hpd_pins: hdmi-hpd { ++ mux { ++ groups = "hdmi_hpd"; ++ function = "hdmi"; ++ bias-disable; ++ }; ++ }; ++ ++ hdmi_i2c_pins: hdmi-i2c { ++ mux { ++ groups = "hdmi_sda", "hdmi_scl"; ++ function = "hdmi"; ++ bias-disable; ++ }; ++ }; ++ + i2c_a_pins: i2c-a { + mux { + groups = "i2c_sda_a", "i2c_sck_a"; +@@ -547,6 +690,16 @@ smp-sram@1ff80 { + }; + }; + ++&cvbs_dac { ++ compatible = "amlogic,meson8-cvbs-dac", "amlogic,meson-cvbs-dac"; ++ ++ clocks = <&clkc CLKID_CTS_VDAC0>; ++ ++ nvmem-cells = <&cvbs_trimming>; ++ nvmem-cell-names = "cvbs_trimming"; ++ ++ status = "okay"; ++}; + + &efuse { + compatible = "amlogic,meson8b-efuse"; +@@ -557,6 +710,10 @@ temperature_calib: calib@1f4 { + /* only the upper two bytes are relevant */ + reg = <0x1f4 0x4>; + }; ++ ++ cvbs_trimming: calib@1f8 { ++ reg = <0x1f8 0x2>; ++ }; + }; + + ðmac { +@@ -586,16 +743,18 @@ &gpio_intc { + }; + + &hhi { +- clkc: clock-controller { ++ clkc: clock-controller@0 { + compatible = "amlogic,meson8b-clkc"; ++ reg = <0x0 0x39c>; + clocks = <&xtal>, <&ddr_clkc DDR_CLKID_DDR_PLL>; + clock-names = "xtal", "ddr_pll"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + +- pwrc: power-controller { ++ pwrc: power-controller@100 { + compatible = "amlogic,meson8b-pwrc"; ++ reg = <0x100 0x10>; + #power-domain-cells = <1>; + amlogic,ao-sysctrl = <&pmu>; + resets = <&reset RESET_DBLK>, +@@ -617,6 +776,14 @@ pwrc: power-controller { + assigned-clocks = <&clkc CLKID_VPU>; + assigned-clock-rates = <182142857>; + }; ++ ++ hdmi_tx_phy: hdmi-phy@3a0 { ++ compatible = "amlogic,meson8b-hdmi-tx-phy", ++ "amlogic,meson8-hdmi-tx-phy"; ++ clocks = <&clkc CLKID_HDMI_PLL_HDMI_OUT>; ++ reg = <0x3a0 0xc>; ++ #phy-cells = <0>; ++ }; + }; + + &hwrng { +diff --git a/arch/arm/boot/dts/amlogic/meson8m2.dtsi b/arch/arm/boot/dts/amlogic/meson8m2.dtsi +index 6725dd9f..fcb2ad97 100644 +--- a/arch/arm/boot/dts/amlogic/meson8m2.dtsi ++++ b/arch/arm/boot/dts/amlogic/meson8m2.dtsi +@@ -96,6 +96,10 @@ &usb1_phy { + compatible = "amlogic,meson8m2-usb2-phy", "amlogic,meson-mx-usb2-phy"; + }; + ++&vpu { ++ compatible = "amlogic,meson8m2-vpu"; ++}; ++ + &wdt { + compatible = "amlogic,meson8m2-wdt", "amlogic,meson8b-wdt"; + }; +diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig +index 615fdd0c..eff3e34b 100644 +--- a/drivers/gpu/drm/meson/Kconfig ++++ b/drivers/gpu/drm/meson/Kconfig +@@ -10,6 +10,7 @@ config DRM_MESON + select REGMAP_MMIO + select MESON_CANVAS + select CEC_CORE if CEC_NOTIFIER ++ imply PHY_MESON_CVBS_DAC + + config DRM_MESON_DW_HDMI + tristate "HDMI Synopsys Controller support for Amlogic Meson Display" +@@ -24,3 +25,11 @@ config DRM_MESON_DW_MIPI_DSI + default y if DRM_MESON + select DRM_DW_MIPI_DSI + select GENERIC_PHY_MIPI_DPHY ++ ++config DRM_MESON_TRANSWITCH_HDMI ++ tristate "Amlogic Meson8/8b/8m2 TranSwitch HDMI 1.4 Controller support" ++ depends on ARM || COMPILE_TEST ++ depends on DRM_MESON ++ default y if DRM_MESON ++ select REGMAP_MMIO ++ select SND_SOC_HDMI_CODEC if SND_SOC +diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile +index 43071bdb..c44cb6c5 100644 +--- a/drivers/gpu/drm/meson/Makefile ++++ b/drivers/gpu/drm/meson/Makefile +@@ -7,3 +7,4 @@ meson-drm-y += meson_encoder_hdmi.o meson_encoder_dsi.o + obj-$(CONFIG_DRM_MESON) += meson-drm.o + obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o + obj-$(CONFIG_DRM_MESON_DW_MIPI_DSI) += meson_dw_mipi_dsi.o ++obj-$(CONFIG_DRM_MESON_TRANSWITCH_HDMI) += meson_transwitch_hdmi.o +diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c +index cb674966..e8134e4c 100644 +--- a/drivers/gpu/drm/meson/meson_drv.c ++++ b/drivers/gpu/drm/meson/meson_drv.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -133,30 +134,147 @@ static struct regmap_config meson_regmap_config = { + .max_register = 0x1000, + }; + ++static int meson_cvbs_dac_phy_init(struct meson_drm *priv) ++{ ++ struct platform_device *pdev; ++ const char *platform_id_name; ++ ++ priv->cvbs_dac = devm_phy_optional_get(priv->dev, "cvbs-dac"); ++ if (IS_ERR(priv->cvbs_dac)) ++ return dev_err_probe(priv->dev, PTR_ERR(priv->cvbs_dac), ++ "Failed to get the 'cvbs-dac' PHY\n"); ++ else if (priv->cvbs_dac) ++ return 0; ++ ++ switch (priv->compat) { ++ case VPU_COMPATIBLE_GXBB: ++ platform_id_name = "meson-gxbb-cvbs-dac"; ++ break; ++ case VPU_COMPATIBLE_GXL: ++ case VPU_COMPATIBLE_GXM: ++ platform_id_name = "meson-gxl-cvbs-dac"; ++ break; ++ case VPU_COMPATIBLE_G12A: ++ platform_id_name = "meson-g12a-cvbs-dac"; ++ break; ++ default: ++ return dev_err_probe(priv->dev, -EINVAL, ++ "No CVBS DAC platform ID found\n"); ++ } ++ ++ pdev = platform_device_register_data(priv->dev, platform_id_name, ++ PLATFORM_DEVID_AUTO, NULL, 0); ++ if (IS_ERR(pdev)) ++ return dev_err_probe(priv->dev, PTR_ERR(pdev), ++ "Failed to register fallback CVBS DAC PHY platform device\n"); ++ ++ priv->cvbs_dac = platform_get_drvdata(pdev); ++ if (IS_ERR(priv->cvbs_dac)) { ++ platform_device_unregister(pdev); ++ return dev_err_probe(priv->dev, PTR_ERR(priv->cvbs_dac), ++ "Failed to get the 'cvbs-dac' PHY from it's platform device\n"); ++ } ++ ++ dev_warn(priv->dev, "Using fallback for old .dtbs without CVBS DAC\n"); ++ ++ priv->cvbs_dac_pdev = pdev; ++ ++ return 0; ++} ++ ++static void meson_cvbs_dac_phy_exit(struct meson_drm *priv) ++{ ++ platform_device_unregister(priv->cvbs_dac_pdev); ++} ++ + static void meson_vpu_init(struct meson_drm *priv) + { +- u32 value; ++ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) { ++ writel(0x0, priv->io_base + _REG(VPU_MEM_PD_REG0)); ++ writel(0x0, priv->io_base + _REG(VPU_MEM_PD_REG1)); ++ } else { ++ u32 value; ++ ++ /* ++ * Slave dc0 and dc5 connected to master port 1. ++ * By default other slaves are connected to master port 0. ++ */ ++ value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) | ++ VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1); ++ writel_relaxed(value, ++ priv->io_base + _REG(VPU_RDARB_MODE_L1C1)); ++ ++ /* Slave dc0 connected to master port 1 */ ++ value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1); ++ writel_relaxed(value, ++ priv->io_base + _REG(VPU_RDARB_MODE_L1C2)); ++ ++ /* Slave dc4 and dc7 connected to master port 1 */ ++ value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) | ++ VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1); ++ writel_relaxed(value, ++ priv->io_base + _REG(VPU_RDARB_MODE_L2C1)); ++ ++ /* Slave dc1 connected to master port 1 */ ++ value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1); ++ writel_relaxed(value, ++ priv->io_base + _REG(VPU_WRARB_MODE_L2C1)); ++ } ++} ++ ++static int meson_video_clock_init(struct meson_drm *priv) ++{ ++ int ret; ++ ++ ret = clk_bulk_prepare(VPU_VID_CLK_NUM, priv->vid_clks); ++ if (ret) ++ return dev_err_probe(priv->dev, ret, ++ "Failed to prepare the video clocks\n"); ++ ++ ret = clk_bulk_prepare(priv->num_intr_clks, priv->intr_clks); ++ if (ret) ++ return dev_err_probe(priv->dev, ret, ++ "Failed to prepare the interrupt clocks\n"); ++ ++ return 0; ++} ++ ++static void meson_video_clock_exit(struct meson_drm *priv) ++{ ++ if (priv->clk_dac_enabled) ++ clk_disable(priv->clk_dac); ++ ++ if (priv->clk_venc_enabled) ++ clk_disable(priv->clk_venc); ++ ++ clk_bulk_unprepare(priv->num_intr_clks, priv->intr_clks); ++ clk_bulk_unprepare(VPU_VID_CLK_NUM, priv->vid_clks); ++} ++ ++static void meson_fbdev_setup(struct meson_drm *priv) ++{ ++ unsigned int preferred_bpp; + + /* +- * Slave dc0 and dc5 connected to master port 1. +- * By default other slaves are connected to master port 0. ++ * All SoC generations before GXBB don't have a way to configure the ++ * alpha value for DRM_FORMAT_XRGB8888 and DRM_FORMAT_XBGR8888. These ++ * formats have an X component instead of an alpha component. On ++ * Meson8/8b/8m2 there is no way to configure the alpha value to use ++ * instead of the X component. This results in the fact that the ++ * formats with X component are only supported on GXBB and newer. Use ++ * 24 bits per pixel and therefore DRM_FORMAT_RGB888 to get a ++ * working framebuffer console. + */ +- value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) | +- VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1); +- writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C1)); +- +- /* Slave dc0 connected to master port 1 */ +- value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1); +- writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C2)); +- +- /* Slave dc4 and dc7 connected to master port 1 */ +- value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) | +- VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1); +- writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L2C1)); +- +- /* Slave dc1 connected to master port 1 */ +- value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1); +- writel_relaxed(value, priv->io_base + _REG(VPU_WRARB_MODE_L2C1)); ++ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) ++ preferred_bpp = 24; ++ else ++ preferred_bpp = 32; ++ ++ drm_fbdev_dma_setup(priv->drm, preferred_bpp); + } + + struct meson_drm_soc_attr { +@@ -165,13 +283,29 @@ struct meson_drm_soc_attr { + }; + + static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = { +- /* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */ ++ /* The maximum frequency of HDMI PHY on Meson8 and Meson8m2 is ~3GHz */ ++ { ++ .limits = { ++ .max_hdmi_phy_freq = 2976000, ++ }, ++ .attrs = (const struct soc_device_attribute []) { ++ { .soc_id = "Meson8 (S802)", }, ++ { .soc_id = "Meson8m2 (S812)", }, ++ { /* sentinel */ }, ++ } ++ }, ++ /* ++ * GXL S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz. ++ * Meson8b (S805) only supports "1200p@60 max resolution" according to ++ * the public datasheet. ++ */ + { + .limits = { + .max_hdmi_phy_freq = 1650000, + }, + .attrs = (const struct soc_device_attribute []) { + { .soc_id = "GXL (S805*)", }, ++ { .soc_id = "Meson8b (S805)", }, + { /* sentinel */ } + } + }, +@@ -212,67 +346,123 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) + priv->compat = match->compat; + priv->afbcd.ops = match->afbcd_ops; + ++ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) { ++ priv->vid_pll_resets[VPU_RESET_VID_PLL_PRE].id = "vid_pll_pre"; ++ priv->vid_pll_resets[VPU_RESET_VID_PLL_POST].id = "vid_pll_post"; ++ priv->vid_pll_resets[VPU_RESET_VID_PLL_SOFT_PRE].id = "vid_pll_soft_pre"; ++ priv->vid_pll_resets[VPU_RESET_VID_PLL_SOFT_POST].id = "vid_pll_soft_post"; ++ ++ ret = devm_reset_control_bulk_get_exclusive(dev, ++ VPU_RESET_VID_PLL_NUM, ++ priv->vid_pll_resets); ++ if (ret) ++ goto free_drm; ++ ++ priv->intr_clks[0].id = "vpu_intr"; ++ priv->intr_clks[1].id = "hdmi_intr_sync"; ++ priv->intr_clks[2].id = "venci_int"; ++ priv->num_intr_clks = 3; ++ ++ ret = devm_clk_bulk_get(dev, priv->num_intr_clks, ++ priv->intr_clks); ++ if (ret) ++ goto free_drm; ++ ++ priv->vid_clks[VPU_VID_CLK_TMDS].id = "tmds"; ++ priv->vid_clks[VPU_VID_CLK_HDMI_TX_PIXEL].id = "hdmi_tx_pixel"; ++ priv->vid_clks[VPU_VID_CLK_CTS_ENCP].id = "cts_encp"; ++ priv->vid_clks[VPU_VID_CLK_CTS_ENCI].id = "cts_enci"; ++ priv->vid_clks[VPU_VID_CLK_CTS_ENCT].id = "cts_enct"; ++ priv->vid_clks[VPU_VID_CLK_CTS_ENCL].id = "cts_encl"; ++ priv->vid_clks[VPU_VID_CLK_CTS_VDAC0].id = "cts_vdac0"; ++ ++ ret = devm_clk_bulk_get(dev, VPU_VID_CLK_NUM, priv->vid_clks); ++ if (ret) ++ goto free_drm; ++ } else { ++ priv->intr_clks[0].id = "vpu_intr"; ++ priv->num_intr_clks = 1; ++ ++ ret = devm_clk_bulk_get_optional(dev, priv->num_intr_clks, ++ priv->intr_clks); ++ if (ret) ++ goto free_drm; ++ } ++ ++ ret = meson_video_clock_init(priv); ++ if (ret) ++ goto free_drm; ++ + regs = devm_platform_ioremap_resource_byname(pdev, "vpu"); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); +- goto free_drm; ++ goto video_clock_exit; + } + + priv->io_base = regs; + ++ /* ++ * The HHI resource is optional because it contains the clocks and CVBS ++ * encoder registers. These are managed by separate drivers though. ++ */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); +- if (!res) { +- ret = -EINVAL; +- goto free_drm; +- } +- /* Simply ioremap since it may be a shared register zone */ +- regs = devm_ioremap(dev, res->start, resource_size(res)); +- if (!regs) { +- ret = -EADDRNOTAVAIL; +- goto free_drm; +- } ++ if (res) { ++ /* Simply ioremap since it may be a shared register zone */ ++ regs = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!regs) { ++ ret = -EADDRNOTAVAIL; ++ goto video_clock_exit; ++ } + +- priv->hhi = devm_regmap_init_mmio(dev, regs, +- &meson_regmap_config); +- if (IS_ERR(priv->hhi)) { +- dev_err(&pdev->dev, "Couldn't create the HHI regmap\n"); +- ret = PTR_ERR(priv->hhi); +- goto free_drm; ++ priv->hhi = devm_regmap_init_mmio(dev, regs, ++ &meson_regmap_config); ++ if (IS_ERR(priv->hhi)) { ++ dev_err(&pdev->dev, ++ "Couldn't create the HHI regmap\n"); ++ ret = PTR_ERR(priv->hhi); ++ goto video_clock_exit; ++ } + } + + priv->canvas = meson_canvas_get(dev); + if (IS_ERR(priv->canvas)) { + ret = PTR_ERR(priv->canvas); +- goto free_drm; ++ goto video_clock_exit; + } + + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); + if (ret) +- goto free_drm; ++ goto video_clock_exit; + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); +- goto free_drm; ++ goto video_clock_exit; + } + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); +- goto free_drm; ++ goto video_clock_exit; + } + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); +- goto free_drm; ++ goto video_clock_exit; + } + ++ ret = meson_cvbs_dac_phy_init(priv); ++ if (ret) ++ goto free_drm; ++ + priv->vsync_irq = platform_get_irq(pdev, 0); + + ret = drm_vblank_init(drm, 1); + if (ret) +- goto free_drm; ++ goto exit_cvbs_dac_phy; + + /* Assign limits per soc revision/package */ + for (i = 0 ; i < ARRAY_SIZE(meson_drm_soc_attrs) ; ++i) { +@@ -288,11 +478,11 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) + */ + ret = drm_aperture_remove_framebuffers(&meson_driver); + if (ret) +- goto free_drm; ++ goto exit_cvbs_dac_phy; + + ret = drmm_mode_config_init(drm); + if (ret) +- goto free_drm; ++ goto exit_cvbs_dac_phy; + drm->mode_config.max_width = 3840; + drm->mode_config.max_height = 2160; + drm->mode_config.funcs = &meson_mode_config_funcs; +@@ -307,7 +497,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) + if (priv->afbcd.ops) { + ret = priv->afbcd.ops->init(priv); + if (ret) +- goto free_drm; ++ goto exit_cvbs_dac_phy; + } + + /* Encoder Initialization */ +@@ -362,7 +552,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) + if (ret) + goto uninstall_irq; + +- drm_fbdev_dma_setup(drm, 32); ++ meson_fbdev_setup(priv); + + return 0; + +@@ -371,6 +561,10 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) + exit_afbcd: + if (priv->afbcd.ops) + priv->afbcd.ops->exit(priv); ++exit_cvbs_dac_phy: ++ meson_cvbs_dac_phy_exit(priv); ++video_clock_exit: ++ meson_video_clock_exit(priv); + free_drm: + drm_dev_put(drm); + +@@ -415,6 +609,10 @@ static void meson_drv_unbind(struct device *dev) + + if (priv->afbcd.ops) + priv->afbcd.ops->exit(priv); ++ ++ meson_cvbs_dac_phy_exit(priv); ++ ++ meson_video_clock_exit(priv); + } + + static const struct component_master_ops meson_drv_master_ops = { +@@ -429,6 +627,8 @@ static int __maybe_unused meson_drv_pm_suspend(struct device *dev) + if (!priv) + return 0; + ++ // TODO: video clock suspend ++ + return drm_mode_config_helper_suspend(priv->drm); + } + +@@ -439,6 +639,7 @@ static int __maybe_unused meson_drv_pm_resume(struct device *dev) + if (!priv) + return 0; + ++ meson_video_clock_init(priv); + meson_vpu_init(priv); + meson_venc_init(priv); + meson_vpp_init(priv); +@@ -521,6 +722,18 @@ static void meson_drv_remove(struct platform_device *pdev) + component_master_del(&pdev->dev, &meson_drv_master_ops); + } + ++static struct meson_drm_match_data meson_drm_m8_data = { ++ .compat = VPU_COMPATIBLE_M8, ++}; ++ ++static struct meson_drm_match_data meson_drm_m8b_data = { ++ .compat = VPU_COMPATIBLE_M8B, ++}; ++ ++static struct meson_drm_match_data meson_drm_m8m2_data = { ++ .compat = VPU_COMPATIBLE_M8M2, ++}; ++ + static struct meson_drm_match_data meson_drm_gxbb_data = { + .compat = VPU_COMPATIBLE_GXBB, + }; +@@ -540,6 +753,12 @@ static struct meson_drm_match_data meson_drm_g12a_data = { + }; + + static const struct of_device_id dt_match[] = { ++ { .compatible = "amlogic,meson8-vpu", ++ .data = (void *)&meson_drm_m8_data }, ++ { .compatible = "amlogic,meson8b-vpu", ++ .data = (void *)&meson_drm_m8b_data }, ++ { .compatible = "amlogic,meson8m2-vpu", ++ .data = (void *)&meson_drm_m8m2_data }, + { .compatible = "amlogic,meson-gxbb-vpu", + .data = (void *)&meson_drm_gxbb_data }, + { .compatible = "amlogic,meson-gxl-vpu", +diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h +index 3f9345c1..59f80fcc 100644 +--- a/drivers/gpu/drm/meson/meson_drv.h ++++ b/drivers/gpu/drm/meson/meson_drv.h +@@ -7,21 +7,28 @@ + #ifndef __MESON_DRV_H + #define __MESON_DRV_H + ++#include + #include + #include + #include ++#include + + struct drm_crtc; + struct drm_device; + struct drm_plane; + struct meson_drm; + struct meson_afbcd_ops; ++struct phy; ++struct platform_device; + + enum vpu_compatible { +- VPU_COMPATIBLE_GXBB = 0, +- VPU_COMPATIBLE_GXL = 1, +- VPU_COMPATIBLE_GXM = 2, +- VPU_COMPATIBLE_G12A = 3, ++ VPU_COMPATIBLE_M8 = 0, ++ VPU_COMPATIBLE_M8B = 1, ++ VPU_COMPATIBLE_M8M2 = 2, ++ VPU_COMPATIBLE_GXBB = 3, ++ VPU_COMPATIBLE_GXL = 4, ++ VPU_COMPATIBLE_GXM = 5, ++ VPU_COMPATIBLE_G12A = 6, + }; + + enum { +@@ -40,6 +47,25 @@ struct meson_drm_soc_limits { + unsigned int max_hdmi_phy_freq; + }; + ++enum vpu_bulk_clk_id { ++ VPU_VID_CLK_TMDS = 0, ++ VPU_VID_CLK_HDMI_TX_PIXEL, ++ VPU_VID_CLK_CTS_ENCP, ++ VPU_VID_CLK_CTS_ENCI, ++ VPU_VID_CLK_CTS_ENCT, ++ VPU_VID_CLK_CTS_ENCL, ++ VPU_VID_CLK_CTS_VDAC0, ++ VPU_VID_CLK_NUM ++}; ++ ++enum vpu_bulk_vid_pll_reset_id { ++ VPU_RESET_VID_PLL_PRE = 0, ++ VPU_RESET_VID_PLL_POST, ++ VPU_RESET_VID_PLL_SOFT_PRE, ++ VPU_RESET_VID_PLL_SOFT_POST, ++ VPU_RESET_VID_PLL_NUM ++}; ++ + struct meson_drm { + struct device *dev; + enum vpu_compatible compat; +@@ -61,6 +87,21 @@ struct meson_drm { + + const struct meson_drm_soc_limits *limits; + ++ struct phy *cvbs_dac; ++ bool cvbs_dac_enabled; ++ struct platform_device *cvbs_dac_pdev; ++ ++ struct clk_bulk_data intr_clks[3]; ++ unsigned int num_intr_clks; ++ bool intr_clks_enabled; ++ struct clk_bulk_data vid_clks[VPU_VID_CLK_NUM]; ++ bool vid_clk_rate_exclusive[VPU_VID_CLK_NUM]; ++ struct clk *clk_venc; ++ bool clk_venc_enabled; ++ struct clk *clk_dac; ++ bool clk_dac_enabled; ++ struct reset_control_bulk_data vid_pll_resets[VPU_RESET_VID_PLL_NUM]; ++ + /* Components Data */ + struct { + bool osd1_enabled; +diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c +index 34074504..44c09f8b 100644 +--- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c ++++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c +@@ -11,6 +11,7 @@ + + #include + #include ++#include + + #include + #include +@@ -24,12 +25,6 @@ + #include "meson_vclk.h" + #include "meson_encoder_cvbs.h" + +-/* HHI VDAC Registers */ +-#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ +-#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbd offset in data sheet */ +-#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ +-#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbe offset in data sheet */ +- + struct meson_encoder_cvbs { + struct drm_encoder encoder; + struct drm_bridge bridge; +@@ -87,11 +82,28 @@ static int meson_encoder_cvbs_attach(struct drm_bridge *bridge, + { + struct meson_encoder_cvbs *meson_encoder_cvbs = + bridge_to_meson_encoder_cvbs(bridge); ++ int ret; ++ ++ ret = phy_init(meson_encoder_cvbs->priv->cvbs_dac); ++ if (ret) ++ return ret; + + return drm_bridge_attach(bridge->encoder, meson_encoder_cvbs->next_bridge, + &meson_encoder_cvbs->bridge, flags); + } + ++static void meson_encoder_cvbs_detach(struct drm_bridge *bridge) ++{ ++ struct meson_encoder_cvbs *meson_encoder_cvbs = ++ bridge_to_meson_encoder_cvbs(bridge); ++ int ret; ++ ++ ret = phy_exit(meson_encoder_cvbs->priv->cvbs_dac); ++ if (ret) ++ dev_err(meson_encoder_cvbs->priv->dev, ++ "Failed to exit the CVBS DAC\n"); ++} ++ + static int meson_encoder_cvbs_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector) + { +@@ -148,6 +160,7 @@ static void meson_encoder_cvbs_atomic_enable(struct drm_bridge *bridge, + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct drm_connector *connector; ++ int ret; + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (WARN_ON(!connector)) +@@ -177,16 +190,13 @@ static void meson_encoder_cvbs_atomic_enable(struct drm_bridge *bridge, + writel_bits_relaxed(VENC_VDAC_SEL_ATV_DMD, 0, + priv->io_base + _REG(VENC_VDAC_DACSEL0)); + +- if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) { +- regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1); +- regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0); +- } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || +- meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) { +- regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001); +- regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0); +- } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { +- regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0x906001); +- regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0); ++ if (!priv->cvbs_dac_enabled) { ++ ret = phy_power_on(priv->cvbs_dac); ++ if (ret) ++ dev_err(priv->dev, ++ "Failed to power on the CVBS DAC\n"); ++ else ++ priv->cvbs_dac_enabled = true; + } + } + +@@ -196,19 +206,22 @@ static void meson_encoder_cvbs_atomic_disable(struct drm_bridge *bridge, + struct meson_encoder_cvbs *meson_encoder_cvbs = + bridge_to_meson_encoder_cvbs(bridge); + struct meson_drm *priv = meson_encoder_cvbs->priv; ++ int ret; + +- /* Disable CVBS VDAC */ +- if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { +- regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0); +- regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0); +- } else { +- regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); +- regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8); +- } ++ if (!priv->cvbs_dac_enabled) ++ return; ++ ++ ret = phy_power_off(priv->cvbs_dac); ++ if (ret) ++ dev_err(priv->dev, ++ "Failed to power off the CVBS DAC\n"); ++ else ++ priv->cvbs_dac_enabled = false; + } + + static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = { + .attach = meson_encoder_cvbs_attach, ++ .detach = meson_encoder_cvbs_detach, + .mode_valid = meson_encoder_cvbs_mode_valid, + .get_modes = meson_encoder_cvbs_get_modes, + .atomic_enable = meson_encoder_cvbs_atomic_enable, +diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c +index c4686568..583f2924 100644 +--- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c ++++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c +@@ -190,13 +190,13 @@ static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge, + { + struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); + struct drm_atomic_state *state = bridge_state->base.state; +- unsigned int ycrcb_map = VPU_HDMI_OUTPUT_CBYCR; + struct meson_drm *priv = encoder_hdmi->priv; + struct drm_connector_state *conn_state; + const struct drm_display_mode *mode; + struct drm_crtc_state *crtc_state; + struct drm_connector *connector; + bool yuv420_mode = false; ++ unsigned int ycrcb_map; + int vic; + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); +@@ -217,7 +217,14 @@ static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge, + + dev_dbg(priv->dev, "\"%s\" vic %d\n", mode->name, vic); + +- if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) { ++ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) { ++ if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_RGB888_1X24) ++ ycrcb_map = VPU_HDMI_OUTPUT_YCBCR; ++ else ++ ycrcb_map = VPU_HDMI_OUTPUT_CRYCB; ++ } else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) { + ycrcb_map = VPU_HDMI_OUTPUT_CRYCB; + yuv420_mode = true; + } else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16) +@@ -229,17 +236,22 @@ static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge, + /* VCLK Set clock */ + meson_encoder_hdmi_set_vclk(encoder_hdmi, mode); + +- if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) +- /* Setup YUV420 to HDMI-TX, no 10bit diphering */ +- writel_relaxed(2 | (2 << 2), +- priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); +- else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16) +- /* Setup YUV422 to HDMI-TX, no 10bit diphering */ +- writel_relaxed(1 | (2 << 2), +- priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); +- else +- /* Setup YUV444 to HDMI-TX, no 10bit diphering */ +- writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); ++ if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) && ++ !meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) && ++ !meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) { ++ if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) ++ /* Setup YUV420 to HDMI-TX, no 10bit diphering */ ++ writel_relaxed(2 | (2 << 2), ++ priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); ++ else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16) ++ /* Setup YUV422 to HDMI-TX, no 10bit diphering */ ++ writel_relaxed(1 | (2 << 2), ++ priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); ++ else ++ /* Setup YUV444 to HDMI-TX, no 10bit diphering */ ++ writel_relaxed(0, ++ priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); ++ } + + dev_dbg(priv->dev, "%s\n", priv->venc.hdmi_use_enci ? "VENCI" : "VENCP"); + +@@ -262,7 +274,11 @@ static void meson_encoder_hdmi_atomic_disable(struct drm_bridge *bridge, + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); + } + +-static const u32 meson_encoder_hdmi_out_bus_fmts[] = { ++static const u32 meson8_encoder_hdmi_out_bus_fmts[] = { ++ MEDIA_BUS_FMT_YUV8_1X24, ++}; ++ ++static const u32 meson_gx_encoder_hdmi_out_bus_fmts[] = { + MEDIA_BUS_FMT_YUV8_1X24, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_UYYVYY8_0_5X24, +@@ -276,13 +292,27 @@ meson_encoder_hdmi_get_inp_bus_fmts(struct drm_bridge *bridge, + u32 output_fmt, + unsigned int *num_input_fmts) + { ++ struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); ++ struct meson_drm *priv = encoder_hdmi->priv; ++ unsigned int num_out_bus_fmts; ++ const u32 *out_bus_fmts; + u32 *input_fmts = NULL; + int i; + ++ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) { ++ num_out_bus_fmts = ARRAY_SIZE(meson8_encoder_hdmi_out_bus_fmts); ++ out_bus_fmts = meson8_encoder_hdmi_out_bus_fmts; ++ } else { ++ num_out_bus_fmts = ARRAY_SIZE(meson_gx_encoder_hdmi_out_bus_fmts); ++ out_bus_fmts = meson_gx_encoder_hdmi_out_bus_fmts; ++ } ++ + *num_input_fmts = 0; + +- for (i = 0 ; i < ARRAY_SIZE(meson_encoder_hdmi_out_bus_fmts) ; ++i) { +- if (output_fmt == meson_encoder_hdmi_out_bus_fmts[i]) { ++ for (i = 0 ; i < num_out_bus_fmts ; ++i) { ++ if (output_fmt == out_bus_fmts[i]) { + *num_input_fmts = 1; + input_fmts = kcalloc(*num_input_fmts, + sizeof(*input_fmts), +@@ -436,8 +466,11 @@ int meson_encoder_hdmi_init(struct meson_drm *priv) + + drm_connector_attach_max_bpc_property(meson_encoder_hdmi->connector, 8, 8); + +- /* Handle this here until handled by drm_bridge_connector_init() */ +- meson_encoder_hdmi->connector->ycbcr_420_allowed = true; ++ if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) && ++ !meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) && ++ !meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) ++ /* Handle this here until handled by drm_bridge_connector_init() */ ++ meson_encoder_hdmi->connector->ycbcr_420_allowed = true; + + pdev = of_find_device_by_node(remote); + of_node_put(remote); +diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c +index 815dfe30..27e39577 100644 +--- a/drivers/gpu/drm/meson/meson_plane.c ++++ b/drivers/gpu/drm/meson/meson_plane.c +@@ -200,8 +200,11 @@ static void meson_plane_atomic_update(struct drm_plane *plane, + priv->viu.osd1_ctrl_stat2 &= ~OSD_DPATH_MALI_AFBCD; + } + +- /* On GXBB, Use the old non-HDR RGB2YUV converter */ +- if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) ++ /* On GXBB and earlier, Use the old non-HDR RGB2YUV converter */ ++ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) + priv->viu.osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB; + + if (priv->viu.osd1_afbcd && +@@ -471,7 +474,20 @@ static const struct drm_plane_funcs meson_plane_funcs = { + .format_mod_supported = meson_plane_format_mod_supported, + }; + +-static const uint32_t supported_drm_formats[] = { ++/* ++ * X components (for example in DRM_FORMAT_XRGB8888 and DRM_FORMAT_XBGR8888) ++ * are not supported because these older SoC's are lacking the OSD_REPLACE_EN ++ * bit to replace the X alpha component with a static value, leaving the alpha ++ * component in an undefined state. ++ */ ++static const uint32_t supported_drm_formats_m8[] = { ++ DRM_FORMAT_ARGB8888, ++ DRM_FORMAT_ABGR8888, ++ DRM_FORMAT_RGB888, ++ DRM_FORMAT_RGB565, ++}; ++ ++static const uint32_t supported_drm_formats_gx[] = { + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB8888, +@@ -533,6 +549,8 @@ int meson_plane_create(struct meson_drm *priv) + { + struct meson_plane *meson_plane; + struct drm_plane *plane; ++ unsigned int num_drm_formats; ++ const uint32_t *drm_formats; + const uint64_t *format_modifiers = format_modifiers_default; + + meson_plane = devm_kzalloc(priv->drm->dev, sizeof(*meson_plane), +@@ -548,10 +566,19 @@ int meson_plane_create(struct meson_drm *priv) + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + format_modifiers = format_modifiers_afbc_g12a; + ++ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) { ++ drm_formats = supported_drm_formats_m8; ++ num_drm_formats = ARRAY_SIZE(supported_drm_formats_m8); ++ } else { ++ drm_formats = supported_drm_formats_gx; ++ num_drm_formats = ARRAY_SIZE(supported_drm_formats_gx); ++ } ++ + drm_universal_plane_init(priv->drm, plane, 0xFF, + &meson_plane_funcs, +- supported_drm_formats, +- ARRAY_SIZE(supported_drm_formats), ++ drm_formats, num_drm_formats, + format_modifiers, + DRM_PLANE_TYPE_PRIMARY, "meson_primary_plane"); + +diff --git a/drivers/gpu/drm/meson/meson_transwitch_hdmi.c b/drivers/gpu/drm/meson/meson_transwitch_hdmi.c +new file mode 100644 +index 00000000..e88bdba7 +--- /dev/null ++++ b/drivers/gpu/drm/meson/meson_transwitch_hdmi.c +@@ -0,0 +1,1579 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2021 Martin Blumenstingl ++ * ++ * All registers and magic values are taken from Amlogic's GPL kernel sources: ++ * Copyright (C) 2010 Amlogic, Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#include "meson_transwitch_hdmi.h" ++ ++#define HDMI_ADDR_PORT 0x0 ++#define HDMI_DATA_PORT 0x4 ++#define HDMI_CTRL_PORT 0x8 ++ #define HDMI_CTRL_PORT_APB3_ERR_EN BIT(15) ++ ++struct meson_txc_hdmi { ++ struct device *dev; ++ ++ struct regmap *regmap; ++ ++ struct clk *pclk; ++ struct clk *sys_clk; ++ ++ struct phy *phy; ++ bool phy_is_on; ++ ++ struct mutex codec_mutex; ++ enum drm_connector_status last_connector_status; ++ hdmi_codec_plugged_cb codec_plugged_cb; ++ struct device *codec_dev; ++ ++ struct platform_device *hdmi_codec_pdev; ++ ++ struct drm_connector *current_connector; ++ ++ struct drm_bridge bridge; ++ struct drm_bridge *next_bridge; ++ ++ bool sink_is_hdmi; ++}; ++ ++#define bridge_to_meson_txc_hdmi(x) container_of(x, struct meson_txc_hdmi, bridge) ++ ++static const struct regmap_range meson_txc_hdmi_regmap_ranges[] = { ++ regmap_reg_range(0x0000, 0x07ff), ++ regmap_reg_range(0x8000, 0x800c), ++}; ++ ++static const struct regmap_access_table meson_txc_hdmi_regmap_access = { ++ .yes_ranges = meson_txc_hdmi_regmap_ranges, ++ .n_yes_ranges = ARRAY_SIZE(meson_txc_hdmi_regmap_ranges), ++}; ++ ++static int meson_txc_hdmi_reg_read(void *context, unsigned int addr, ++ unsigned int *data) ++{ ++ void __iomem *base = context; ++ ++ writel(addr, base + HDMI_ADDR_PORT); ++ writel(addr, base + HDMI_ADDR_PORT); ++ ++ *data = readl(base + HDMI_DATA_PORT); ++ ++ return 0; ++} ++ ++static int meson_txc_hdmi_reg_write(void *context, unsigned int addr, ++ unsigned int data) ++{ ++ void __iomem *base = context; ++ ++ writel(addr, base + HDMI_ADDR_PORT); ++ writel(addr, base + HDMI_ADDR_PORT); ++ ++ writel(data, base + HDMI_DATA_PORT); ++ ++ return 0; ++} ++ ++static const struct regmap_config meson_txc_hdmi_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 16, ++ .reg_stride = 1, ++ .reg_read = meson_txc_hdmi_reg_read, ++ .reg_write = meson_txc_hdmi_reg_write, ++ .rd_table = &meson_txc_hdmi_regmap_access, ++ .wr_table = &meson_txc_hdmi_regmap_access, ++ .max_register = HDMI_OTHER_RX_PACKET_INTR_CLR, ++ .fast_io = true, ++}; ++ ++static void meson_txc_hdmi_write_infoframe(struct regmap *regmap, ++ unsigned int tx_pkt_reg, u8 *buf, ++ unsigned int len, bool enable) ++{ ++ unsigned int i; ++ ++ /* Write the data bytes by starting at register offset 1 */ ++ for (i = HDMI_INFOFRAME_HEADER_SIZE; i < len; i++) ++ regmap_write(regmap, ++ tx_pkt_reg + i - HDMI_INFOFRAME_HEADER_SIZE + 1, ++ buf[i]); ++ ++ /* Zero all remaining data bytes */ ++ for (; i < 0x1c; i++) ++ regmap_write(regmap, tx_pkt_reg + i, 0x00); ++ ++ /* Write the header (which we skipped above) */ ++ regmap_write(regmap, tx_pkt_reg + 0x00, buf[3]); ++ regmap_write(regmap, tx_pkt_reg + 0x1c, buf[0]); ++ regmap_write(regmap, tx_pkt_reg + 0x1d, buf[1]); ++ regmap_write(regmap, tx_pkt_reg + 0x1e, buf[2]); ++ ++ regmap_write(regmap, tx_pkt_reg + 0x1f, enable ? 0xff : 0x00); ++} ++ ++static void meson_txc_hdmi_disable_infoframe(struct meson_txc_hdmi *priv, ++ unsigned int tx_pkt_reg) ++{ ++ u8 buf[HDMI_INFOFRAME_HEADER_SIZE] = { 0 }; ++ ++ meson_txc_hdmi_write_infoframe(priv->regmap, tx_pkt_reg, buf, ++ HDMI_INFOFRAME_HEADER_SIZE, false); ++} ++ ++static void meson_txc_hdmi_sys5_reset_assert(struct meson_txc_hdmi *priv) ++{ ++ /* A comment in the vendor driver says: bit5,6 is converted */ ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH3_RST_IN | ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH0_RST_IN); ++ usleep_range(10, 20); ++ ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH2_RST_IN | ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH1_RST_IN); ++ usleep_range(10, 20); ++ ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, ++ TX_SYS5_TX_SOFT_RESET_1_TX_PIXEL_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_TMDS_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_MASTER_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_RESAMPLE_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_I2S_RESET_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH2 | ++ TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH1 | ++ TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH0); ++ usleep_range(10, 20); ++} ++ ++static void meson_txc_hdmi_sys5_reset_deassert(struct meson_txc_hdmi *priv) ++{ ++ /* Release the resets except tmds_clk */ ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, ++ TX_SYS5_TX_SOFT_RESET_1_TX_TMDS_RSTN); ++ usleep_range(10, 20); ++ ++ /* Release the tmds_clk reset as well */ ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, 0x0); ++ usleep_range(10, 20); ++ ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH2_RST_IN | ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH1_RST_IN | ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_SR_RST); ++ usleep_range(10, 20); ++ ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH2_RST_IN | ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH1_RST_IN); ++ usleep_range(10, 20); ++} ++ ++static void meson_txc_hdmi_config_hdcp_registers(struct meson_txc_hdmi *priv) ++{ ++ regmap_write(priv->regmap, TX_HDCP_CONFIG0, ++ FIELD_PREP(TX_HDCP_CONFIG0_ROM_ENCRYPT_OFF, 0x3)); ++ regmap_write(priv->regmap, TX_HDCP_MEM_CONFIG, 0x0); ++ regmap_write(priv->regmap, TX_HDCP_ENCRYPT_BYTE, 0x0); ++ ++ regmap_write(priv->regmap, TX_HDCP_MODE, TX_HDCP_MODE_CLEAR_AVMUTE); ++ ++ regmap_write(priv->regmap, TX_HDCP_MODE, TX_HDCP_MODE_ESS_CONFIG); ++} ++ ++static u8 meson_txc_hdmi_bus_fmt_to_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: ++ /* 8 bit */ ++ return 0x0; ++ ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ /* 10 bit */ ++ return 0x1; ++ ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ /* 12 bit */ ++ return 0x2; ++ ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ /* 16 bit */ ++ return 0x3; ++ ++ default: ++ /* unknown, default to 8 bit */ ++ return 0x0; ++ } ++} ++ ++static u8 meson_txc_hdmi_bus_fmt_to_color_format(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: ++ /* Documented as YCbCr444 */ ++ return 0x1; ++ ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ /* Documented as YCbCr422 */ ++ return 0x3; ++ ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ default: ++ /* Documented as RGB444 */ ++ return 0x0; ++ } ++} ++ ++static void meson_txc_hdmi_config_color_space(struct meson_txc_hdmi *priv, ++ unsigned int input_bus_format, ++ unsigned int output_bus_format, ++ enum hdmi_quantization_range quant_range, ++ enum hdmi_colorimetry colorimetry) ++{ ++ unsigned int regval; ++ ++ regmap_write(priv->regmap, TX_VIDEO_DTV_MODE, ++ FIELD_PREP(TX_VIDEO_DTV_MODE_COLOR_DEPTH, ++ meson_txc_hdmi_bus_fmt_to_color_depth(output_bus_format))); ++ ++ regmap_write(priv->regmap, TX_VIDEO_DTV_OPTION_L, ++ FIELD_PREP(TX_VIDEO_DTV_OPTION_L_OUTPUT_COLOR_FORMAT, ++ meson_txc_hdmi_bus_fmt_to_color_format(output_bus_format)) | ++ FIELD_PREP(TX_VIDEO_DTV_OPTION_L_INPUT_COLOR_FORMAT, ++ meson_txc_hdmi_bus_fmt_to_color_format(input_bus_format)) | ++ FIELD_PREP(TX_VIDEO_DTV_OPTION_L_OUTPUT_COLOR_DEPTH, ++ meson_txc_hdmi_bus_fmt_to_color_depth(output_bus_format)) | ++ FIELD_PREP(TX_VIDEO_DTV_OPTION_L_INPUT_COLOR_DEPTH, ++ meson_txc_hdmi_bus_fmt_to_color_depth(input_bus_format))); ++ ++ if (quant_range == HDMI_QUANTIZATION_RANGE_LIMITED) ++ regval = FIELD_PREP(TX_VIDEO_DTV_OPTION_H_OUTPUT_COLOR_RANGE, ++ TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_16_235) | ++ FIELD_PREP(TX_VIDEO_DTV_OPTION_H_INPUT_COLOR_RANGE, ++ TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_16_235); ++ else ++ regval = FIELD_PREP(TX_VIDEO_DTV_OPTION_H_OUTPUT_COLOR_RANGE, ++ TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_0_255) | ++ FIELD_PREP(TX_VIDEO_DTV_OPTION_H_INPUT_COLOR_RANGE, ++ TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_0_255); ++ ++ regmap_write(priv->regmap, TX_VIDEO_DTV_OPTION_H, regval); ++ ++ if (colorimetry == HDMI_COLORIMETRY_ITU_601) { ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_B0, 0x2f); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_B1, 0x1d); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_R0, 0x8b); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_R1, 0x4c); ++ ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CB0, 0x18); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CB1, 0x58); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CR0, 0xd0); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CR1, 0xb6); ++ } else { ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_B0, 0x7b); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_B1, 0x12); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_R0, 0x6c); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_R1, 0x36); ++ ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CB0, 0xf2); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CB1, 0x2f); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CR0, 0xd4); ++ regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CR1, 0x77); ++ } ++} ++ ++static void meson_txc_hdmi_config_serializer_clock(struct meson_txc_hdmi *priv, ++ enum hdmi_colorimetry colorimetry) ++{ ++ /* Serializer Internal clock setting */ ++ if (colorimetry == HDMI_COLORIMETRY_ITU_601) ++ regmap_write(priv->regmap, TX_SYS1_PLL, 0x24); ++ else ++ regmap_write(priv->regmap, TX_SYS1_PLL, 0x22); ++ ++#if 0 ++ // TODO: not ported yet ++ if ((param->VIC==HDMI_1080p60)&&(param->color_depth==COLOR_30BIT)&&(hdmi_rd_reg(0x018)==0x22)) { ++ regmap_write(priv->regmap, TX_SYS1_PLL, 0x12); ++ } ++#endif ++} ++ ++static void meson_txc_hdmi_reconfig_packet_setting(struct meson_txc_hdmi *priv, ++ u8 cea_mode) ++{ ++ u8 alloc_active2, alloc_eof1, alloc_sof1, alloc_sof2; ++ ++ regmap_write(priv->regmap, TX_PACKET_CONTROL_1, ++ FIELD_PREP(TX_PACKET_CONTROL_1_PACKET_START_LATENCY, 58)); ++ regmap_write(priv->regmap, TX_PACKET_CONTROL_2, ++ TX_PACKET_CONTROL_2_HORIZONTAL_GC_PACKET_TRANSPORT_EN); ++ ++ switch (cea_mode) { ++ case 31: ++ /* 1920x1080p50 */ ++ alloc_active2 = 0x12; ++ alloc_eof1 = 0x10; ++ alloc_sof1 = 0xb6; ++ alloc_sof2 = 0x11; ++ break; ++ case 93: ++ /* 3840x2160p24 */ ++ alloc_active2 = 0x12; ++ alloc_eof1 = 0x47; ++ alloc_sof1 = 0xf8; ++ alloc_sof2 = 0x52; ++ break; ++ case 94: ++ /* 3840x2160p25 */ ++ alloc_active2 = 0x12; ++ alloc_eof1 = 0x44; ++ alloc_sof1 = 0xda; ++ alloc_sof2 = 0x52; ++ break; ++ case 95: ++ /* 3840x2160p30 */ ++ alloc_active2 = 0x0f; ++ alloc_eof1 = 0x3a; ++ alloc_sof1 = 0x60; ++ alloc_sof2 = 0x52; ++ break; ++ case 98: ++ /* 4096x2160p24 */ ++ alloc_active2 = 0x12; ++ alloc_eof1 = 0x47; ++ alloc_sof1 = 0xf8; ++ alloc_sof2 = 0x52; ++ break; ++ default: ++ /* Disable the special packet settings only */ ++ regmap_write(priv->regmap, TX_PACKET_ALLOC_ACTIVE_1, 0x00); ++ return; ++ } ++ ++ /* ++ * The vendor driver says: manually configure these register to get ++ * stable video timings. ++ */ ++ regmap_write(priv->regmap, TX_PACKET_ALLOC_ACTIVE_1, 0x01); ++ regmap_write(priv->regmap, TX_PACKET_ALLOC_ACTIVE_2, alloc_active2); ++ regmap_write(priv->regmap, TX_PACKET_ALLOC_EOF_1, alloc_eof1); ++ regmap_write(priv->regmap, TX_PACKET_ALLOC_EOF_2, 0x12); ++ regmap_write(priv->regmap, TX_CORE_ALLOC_VSYNC_0, 0x01); ++ regmap_write(priv->regmap, TX_CORE_ALLOC_VSYNC_1, 0x00); ++ regmap_write(priv->regmap, TX_CORE_ALLOC_VSYNC_2, 0x0a); ++ regmap_write(priv->regmap, TX_PACKET_ALLOC_SOF_1, alloc_sof1); ++ regmap_write(priv->regmap, TX_PACKET_ALLOC_SOF_2, alloc_sof2); ++ regmap_update_bits(priv->regmap, TX_PACKET_CONTROL_1, ++ TX_PACKET_CONTROL_1_FORCE_PACKET_TIMING, ++ TX_PACKET_CONTROL_1_FORCE_PACKET_TIMING); ++} ++ ++static void meson_txc_hdmi_set_avi_infoframe(struct meson_txc_hdmi *priv, ++ struct drm_connector *conn, ++ const struct drm_display_mode *mode, ++ const struct drm_connector_state *conn_state, ++ unsigned int output_bus_format, ++ enum hdmi_quantization_range quant_range, ++ enum hdmi_colorimetry colorimetry) ++{ ++ u8 buf[HDMI_INFOFRAME_SIZE(AVI)], *video_code; ++ struct hdmi_avi_infoframe frame; ++ int ret; ++ ++ ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, conn, mode); ++ if (ret < 0) { ++ drm_err(priv->bridge.dev, ++ "Failed to setup AVI infoframe: %d\n", ret); ++ return; ++ } ++ ++ switch (output_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: ++ frame.colorspace = HDMI_COLORSPACE_YUV444; ++ break; ++ ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ frame.colorspace = HDMI_COLORSPACE_YUV422; ++ break; ++ ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ default: ++ frame.colorspace = HDMI_COLORSPACE_RGB; ++ break; ++ } ++ ++ drm_hdmi_avi_infoframe_colorimetry(&frame, conn_state); ++ drm_hdmi_avi_infoframe_quant_range(&frame, conn, mode, quant_range); ++ drm_hdmi_avi_infoframe_bars(&frame, conn_state); ++ ++ ret = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf)); ++ if (ret < 0) { ++ drm_err(priv->bridge.dev, ++ "Failed to pack AVI infoframe: %d\n", ret); ++ return; ++ } ++ ++ video_code = &buf[HDMI_INFOFRAME_HEADER_SIZE + 3]; ++ if (*video_code > 108) { ++ regmap_write(priv->regmap, TX_PKT_REG_EXCEPT0_BASE_ADDR, ++ *video_code); ++ *video_code = 0x00; ++ } else { ++ regmap_write(priv->regmap, TX_PKT_REG_EXCEPT0_BASE_ADDR, ++ 0x00); ++ } ++ ++ meson_txc_hdmi_write_infoframe(priv->regmap, ++ TX_PKT_REG_AVI_INFO_BASE_ADDR, buf, ++ sizeof(buf), true); ++} ++ ++static void meson_txc_hdmi_set_vendor_infoframe(struct meson_txc_hdmi *priv, ++ struct drm_connector *conn, ++ const struct drm_display_mode *mode) ++{ ++ u8 buf[HDMI_INFOFRAME_HEADER_SIZE + 6]; ++ struct hdmi_vendor_infoframe frame; ++ int ret; ++ ++ ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame, conn, mode); ++ if (ret) { ++ drm_dbg(priv->bridge.dev, ++ "Failed to setup vendor infoframe: %d\n", ret); ++ return; ++ } ++ ++ ret = hdmi_vendor_infoframe_pack(&frame, buf, sizeof(buf)); ++ if (ret < 0) { ++ drm_err(priv->bridge.dev, ++ "Failed to pack vendor infoframe: %d\n", ret); ++ return; ++ } ++ ++ meson_txc_hdmi_write_infoframe(priv->regmap, ++ TX_PKT_REG_VEND_INFO_BASE_ADDR, buf, ++ sizeof(buf), true); ++} ++ ++static void meson_txc_hdmi_set_spd_infoframe(struct meson_txc_hdmi *priv) ++{ ++ u8 buf[HDMI_INFOFRAME_SIZE(SPD)]; ++ struct hdmi_spd_infoframe frame; ++ int ret; ++ ++ ret = hdmi_spd_infoframe_init(&frame, "Amlogic", "Meson TXC HDMI"); ++ if (ret < 0) { ++ drm_err(priv->bridge.dev, ++ "Failed to setup SPD infoframe: %d\n", ret); ++ return; ++ } ++ ++ ret = hdmi_spd_infoframe_pack(&frame, buf, sizeof(buf)); ++ if (ret < 0) { ++ drm_err(priv->bridge.dev, ++ "Failed to pack SDP infoframe: %d\n", ret); ++ return; ++ } ++ ++ meson_txc_hdmi_write_infoframe(priv->regmap, ++ TX_PKT_REG_SPD_INFO_BASE_ADDR, buf, ++ sizeof(buf), true); ++} ++ ++static void meson_txc_hdmi_handle_plugged_change(struct meson_txc_hdmi *priv) ++{ ++ bool plugged; ++ ++ plugged = priv->last_connector_status == connector_status_connected; ++ ++ if (priv->codec_dev && priv->codec_plugged_cb) ++ priv->codec_plugged_cb(priv->codec_dev, plugged); ++} ++ ++static int meson_txc_hdmi_bridge_attach(struct drm_bridge *bridge, ++ enum drm_bridge_attach_flags flags) ++{ ++ struct meson_txc_hdmi *priv = bridge->driver_private; ++ ++ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { ++ drm_err(bridge->dev, ++ "DRM_BRIDGE_ATTACH_NO_CONNECTOR flag is not set but needed\n"); ++ return -EINVAL; ++ } ++ ++ return drm_bridge_attach(bridge->encoder, priv->next_bridge, bridge, ++ flags); ++} ++ ++/* Can return a maximum of 11 possible output formats for a mode/connector */ ++#define MAX_OUTPUT_SEL_FORMATS 11 ++ ++static u32 * ++meson_txc_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, ++ struct drm_bridge_state *bridge_state, ++ struct drm_crtc_state *crtc_state, ++ struct drm_connector_state *conn_state, ++ unsigned int *num_output_fmts) ++{ ++ struct drm_connector *conn = conn_state->connector; ++ struct drm_display_info *info = &conn->display_info; ++ u8 max_bpc = conn_state->max_requested_bpc; ++ unsigned int i = 0; ++ u32 *output_fmts; ++ ++ *num_output_fmts = 0; ++ ++ output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts), ++ GFP_KERNEL); ++ if (!output_fmts) ++ return NULL; ++ ++ /* If we are the only bridge, avoid negotiating with ourselves */ ++ if (list_is_singular(&bridge->encoder->bridge_chain)) { ++ *num_output_fmts = 1; ++ output_fmts[0] = MEDIA_BUS_FMT_FIXED; ++ ++ return output_fmts; ++ } ++ ++ /* ++ * Order bus formats from 16bit to 8bit and from YUV422 to RGB ++ * if supported. In any case the default RGB888 format is added ++ */ ++ ++ if (max_bpc >= 16 && info->bpc == 16) { ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; ++ ++ output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; ++ } ++ ++ if (max_bpc >= 12 && info->bpc >= 12) { ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; ++ ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; ++ ++ output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; ++ } ++ ++ if (max_bpc >= 10 && info->bpc >= 10) { ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; ++ ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; ++ ++ output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; ++ } ++ ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; ++ ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; ++ ++ /* Default 8bit RGB fallback */ ++ output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; ++ ++ *num_output_fmts = i; ++ ++ return output_fmts; ++} ++ ++/* Can return a maximum of 3 possible input formats for an output format */ ++#define MAX_INPUT_SEL_FORMATS 3 ++ ++static u32 * ++meson_txc_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, ++ struct drm_bridge_state *bridge_state, ++ struct drm_crtc_state *crtc_state, ++ struct drm_connector_state *conn_state, ++ u32 output_fmt, ++ unsigned int *num_input_fmts) ++{ ++ u32 *input_fmts; ++ unsigned int i = 0; ++ ++ *num_input_fmts = 0; ++ ++ input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), ++ GFP_KERNEL); ++ if (!input_fmts) ++ return NULL; ++ ++ switch (output_fmt) { ++ /* If MEDIA_BUS_FMT_FIXED is tested, return default bus format */ ++ case MEDIA_BUS_FMT_FIXED: ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; ++ break; ++ ++ /* 8bit */ ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; ++ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; ++ break; ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; ++ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; ++ break; ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; ++ break; ++ ++ /* 10bit */ ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; ++ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; ++ break; ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; ++ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; ++ break; ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; ++ break; ++ ++ /* 12bit */ ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; ++ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; ++ break; ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; ++ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; ++ break; ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; ++ break; ++ ++ /* 16bit */ ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; ++ break; ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; ++ input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; ++ break; ++ } ++ ++ *num_input_fmts = i; ++ ++ if (*num_input_fmts == 0) { ++ kfree(input_fmts); ++ input_fmts = NULL; ++ } ++ ++ return input_fmts; ++} ++ ++static void meson_txc_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, ++ struct drm_bridge_state *old_bridge_state) ++{ ++ struct meson_txc_hdmi *priv = bridge_to_meson_txc_hdmi(bridge); ++ struct drm_atomic_state *state = old_bridge_state->base.state; ++ enum hdmi_quantization_range quant_range; ++ struct drm_connector_state *conn_state; ++ struct drm_bridge_state *bridge_state; ++ const struct drm_display_mode *mode; ++ enum hdmi_colorimetry colorimetry; ++ struct drm_crtc_state *crtc_state; ++ struct drm_connector *connector; ++ unsigned int i; ++ u8 cea_mode; ++ ++ bridge_state = drm_atomic_get_new_bridge_state(state, bridge); ++ ++ connector = drm_atomic_get_new_connector_for_encoder(state, ++ bridge->encoder); ++ if (WARN_ON(!connector)) ++ return; ++ ++ conn_state = drm_atomic_get_new_connector_state(state, connector); ++ if (WARN_ON(!conn_state)) ++ return; ++ ++ crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); ++ if (WARN_ON(!crtc_state)) ++ return; ++ ++ priv->current_connector = connector; ++ ++ mode = &crtc_state->adjusted_mode; ++ ++ cea_mode = drm_match_cea_mode(mode); ++ ++ if (priv->sink_is_hdmi) { ++ quant_range = drm_default_rgb_quant_range(mode); ++ ++ switch (cea_mode) { ++ case 2 ... 3: ++ case 6 ... 7: ++ case 17 ... 18: ++ case 21 ... 22: ++ colorimetry = HDMI_COLORIMETRY_ITU_601; ++ break; ++ ++ default: ++ colorimetry = HDMI_COLORIMETRY_ITU_709; ++ break; ++ } ++ ++ meson_txc_hdmi_set_avi_infoframe(priv, connector, mode, ++ conn_state, ++ bridge_state->output_bus_cfg.format, ++ quant_range, colorimetry); ++ meson_txc_hdmi_set_vendor_infoframe(priv, connector, mode); ++ meson_txc_hdmi_set_spd_infoframe(priv); ++ } else { ++ quant_range = HDMI_QUANTIZATION_RANGE_FULL; ++ colorimetry = HDMI_COLORIMETRY_NONE; ++ } ++ ++ meson_txc_hdmi_sys5_reset_assert(priv); ++ ++ meson_txc_hdmi_config_hdcp_registers(priv); ++ ++ if (cea_mode == 39) ++ regmap_write(priv->regmap, TX_VIDEO_DTV_TIMING, 0x0); ++ else ++ regmap_write(priv->regmap, TX_VIDEO_DTV_TIMING, ++ TX_VIDEO_DTV_TIMING_DISABLE_VIC39_CORRECTION); ++ ++ regmap_write(priv->regmap, TX_CORE_DATA_CAPTURE_2, ++ TX_CORE_DATA_CAPTURE_2_INTERNAL_PACKET_ENABLE); ++ regmap_write(priv->regmap, TX_CORE_DATA_MONITOR_1, ++ TX_CORE_DATA_MONITOR_1_LANE0 | ++ FIELD_PREP(TX_CORE_DATA_MONITOR_1_SELECT_LANE0, 0x7)); ++ regmap_write(priv->regmap, TX_CORE_DATA_MONITOR_2, ++ FIELD_PREP(TX_CORE_DATA_MONITOR_2_MONITOR_SELECT, 0x2)); ++ ++ if (priv->sink_is_hdmi) ++ regmap_write(priv->regmap, TX_TMDS_MODE, ++ TX_TMDS_MODE_FORCED_HDMI | ++ TX_TMDS_MODE_HDMI_CONFIG); ++ else ++ regmap_write(priv->regmap, TX_TMDS_MODE, ++ TX_TMDS_MODE_FORCED_HDMI); ++ ++ regmap_write(priv->regmap, TX_SYS4_CONNECT_SEL_1, 0x0); ++ ++ /* ++ * Set tmds_clk pattern to be "0000011111" before being sent to AFE ++ * clock channel. ++ */ ++ regmap_write(priv->regmap, TX_SYS4_CK_INV_VIDEO, ++ TX_SYS4_CK_INV_VIDEO_TMDS_CLK_PATTERN); ++ ++ regmap_write(priv->regmap, TX_SYS5_FIFO_CONFIG, ++ TX_SYS5_FIFO_CONFIG_CLK_CHANNEL3_OUTPUT_ENABLE | ++ TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL2_ENABLE | ++ TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL1_ENABLE | ++ TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL0_ENABLE); ++ ++ meson_txc_hdmi_config_color_space(priv, ++ bridge_state->input_bus_cfg.format, ++ bridge_state->output_bus_cfg.format, ++ quant_range, colorimetry); ++ ++ meson_txc_hdmi_sys5_reset_deassert(priv); ++ ++ meson_txc_hdmi_config_serializer_clock(priv, colorimetry); ++ meson_txc_hdmi_reconfig_packet_setting(priv, cea_mode); ++ ++ /* all resets need to be applied twice */ ++ for (i = 0; i < 2; i++) { ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, ++ TX_SYS5_TX_SOFT_RESET_1_TX_PIXEL_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_TMDS_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_MASTER_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_RESAMPLE_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_I2S_RESET_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH2 | ++ TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH1 | ++ TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH0); ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH3_RST_IN | ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH2_RST_IN | ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH1_RST_IN | ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_CH0_RST_IN | ++ TX_SYS5_TX_SOFT_RESET_2_HDMI_SR_RST | ++ TX_SYS5_TX_SOFT_RESET_2_TX_DDC_HDCP_RSTN | ++ TX_SYS5_TX_SOFT_RESET_2_TX_DDC_EDID_RSTN | ++ TX_SYS5_TX_SOFT_RESET_2_TX_DIG_RESET_N_CH3); ++ usleep_range(5000, 10000); ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, 0x00); ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, 0x00); ++ usleep_range(5000, 10000); ++ } ++ ++ if (!priv->phy_is_on) { ++ int ret; ++ ++ ret = phy_power_on(priv->phy); ++ if (ret) ++ drm_err(bridge->dev, "Failed to turn on PHY\n"); ++ else ++ priv->phy_is_on = true; ++ } ++} ++ ++static void meson_txc_hdmi_bridge_atomic_disable(struct drm_bridge *bridge, ++ struct drm_bridge_state *old_bridge_state) ++{ ++ struct meson_txc_hdmi *priv = bridge_to_meson_txc_hdmi(bridge); ++ ++ priv->current_connector = NULL; ++ ++ if (priv->phy_is_on) { ++ int ret; ++ ++ ret = phy_power_off(priv->phy); ++ if (ret) ++ drm_err(bridge->dev, "Failed to turn off PHY\n"); ++ else ++ priv->phy_is_on = false; ++ } ++ ++ meson_txc_hdmi_disable_infoframe(priv, TX_PKT_REG_AUDIO_INFO_BASE_ADDR); ++ meson_txc_hdmi_disable_infoframe(priv, TX_PKT_REG_AVI_INFO_BASE_ADDR); ++ meson_txc_hdmi_disable_infoframe(priv, TX_PKT_REG_EXCEPT0_BASE_ADDR); ++ meson_txc_hdmi_disable_infoframe(priv, TX_PKT_REG_VEND_INFO_BASE_ADDR); ++} ++ ++static enum drm_mode_status ++meson_txc_hdmi_bridge_mode_valid(struct drm_bridge *bridge, ++ const struct drm_display_info *info, ++ const struct drm_display_mode *mode) ++{ ++ return MODE_OK; ++} ++ ++static enum drm_connector_status meson_txc_hdmi_bridge_detect(struct drm_bridge *bridge) ++{ ++ struct meson_txc_hdmi *priv = bridge_to_meson_txc_hdmi(bridge); ++ enum drm_connector_status status; ++ unsigned int val; ++ ++ regmap_read(priv->regmap, TX_HDCP_ST_EDID_STATUS, &val); ++ if (val & TX_HDCP_ST_EDID_STATUS_HPD_STATUS) ++ status = connector_status_connected; ++ else ++ status = connector_status_disconnected; ++ ++ mutex_lock(&priv->codec_mutex); ++ if (priv->last_connector_status != status) { ++ priv->last_connector_status = status; ++ meson_txc_hdmi_handle_plugged_change(priv); ++ } ++ mutex_unlock(&priv->codec_mutex); ++ ++ return status; ++} ++ ++static int meson_txc_hdmi_get_edid_block(void *data, u8 *buf, unsigned int block, ++ size_t len) ++{ ++ unsigned int i, regval, start = block * EDID_LENGTH; ++ struct meson_txc_hdmi *priv = data; ++ int ret; ++ ++ /* Start the DDC transaction */ ++ regmap_update_bits(priv->regmap, TX_HDCP_EDID_CONFIG, ++ TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG, 0); ++ regmap_update_bits(priv->regmap, TX_HDCP_EDID_CONFIG, ++ TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG, ++ TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG); ++ ++ ret = regmap_read_poll_timeout(priv->regmap, ++ TX_HDCP_ST_EDID_STATUS, ++ regval, ++ (regval & TX_HDCP_ST_EDID_STATUS_EDID_DATA_READY), ++ 1000, 200000); ++ ++ regmap_update_bits(priv->regmap, TX_HDCP_EDID_CONFIG, ++ TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG, 0); ++ ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < len; i++) { ++ regmap_read(priv->regmap, TX_RX_EDID_OFFSET + start + i, ++ ®val); ++ buf[i] = regval; ++ } ++ ++ return 0; ++} ++ ++static struct edid *meson_txc_hdmi_bridge_get_edid(struct drm_bridge *bridge, ++ struct drm_connector *connector) ++{ ++ struct meson_txc_hdmi *priv = bridge_to_meson_txc_hdmi(bridge); ++ struct edid *edid; ++ ++ edid = drm_do_get_edid(connector, meson_txc_hdmi_get_edid_block, priv); ++ if (!edid) { ++ drm_dbg(priv->bridge.dev, "Failed to get EDID\n"); ++ return NULL; ++ } ++ ++ priv->sink_is_hdmi = drm_detect_hdmi_monitor(edid); ++ ++ return edid; ++} ++ ++static const struct drm_bridge_funcs meson_txc_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 = meson_txc_hdmi_bridge_attach, ++ .atomic_get_output_bus_fmts = meson_txc_hdmi_bridge_atomic_get_output_bus_fmts, ++ .atomic_get_input_bus_fmts = meson_txc_hdmi_bridge_atomic_get_input_bus_fmts, ++ .atomic_enable = meson_txc_hdmi_bridge_atomic_enable, ++ .atomic_disable = meson_txc_hdmi_bridge_atomic_disable, ++ .mode_valid = meson_txc_hdmi_bridge_mode_valid, ++ .detect = meson_txc_hdmi_bridge_detect, ++ .get_edid = meson_txc_hdmi_bridge_get_edid, ++}; ++ ++static int meson_txc_hdmi_parse_dt(struct meson_txc_hdmi *priv) ++{ ++ struct device_node *endpoint, *remote; ++ ++ endpoint = of_graph_get_endpoint_by_regs(priv->dev->of_node, 1, -1); ++ if (!endpoint) { ++ dev_err(priv->dev, "Missing endpoint in port@1\n"); ++ return -ENODEV; ++ } ++ ++ remote = of_graph_get_remote_port_parent(endpoint); ++ of_node_put(endpoint); ++ if (!remote) { ++ dev_err(priv->dev, "Endpoint in port@1 unconnected\n"); ++ return -ENODEV; ++ } ++ ++ if (!of_device_is_available(remote)) { ++ dev_err(priv->dev, "port@1 remote device is disabled\n"); ++ of_node_put(remote); ++ return -ENODEV; ++ } ++ ++ priv->next_bridge = of_drm_find_bridge(remote); ++ of_node_put(remote); ++ if (!priv->next_bridge) ++ return -EPROBE_DEFER; ++ ++ return 0; ++} ++ ++static int meson_txc_hdmi_hw_init(struct meson_txc_hdmi *priv) ++{ ++ unsigned long ddc_i2c_bus_clk_hz = 500 * 1000; ++ unsigned long sys_clk_hz = 24 * 1000 * 1000; ++ int ret; ++ ++ ret = phy_init(priv->phy); ++ if (ret) { ++ dev_err(priv->dev, "Failed to initialize the PHY: %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_set_rate(priv->sys_clk, sys_clk_hz); ++ if (ret) { ++ dev_err(priv->dev, "Failed to set HDMI system clock to 24MHz\n"); ++ goto err_phy_exit; ++ } ++ ++ ret = clk_prepare_enable(priv->sys_clk); ++ if (ret) { ++ dev_err(priv->dev, "Failed to enable the sys clk\n"); ++ goto err_phy_exit; ++ } ++ ++ regmap_update_bits(priv->regmap, HDMI_OTHER_CTRL1, ++ HDMI_OTHER_CTRL1_POWER_ON, ++ HDMI_OTHER_CTRL1_POWER_ON); ++ ++ regmap_write(priv->regmap, TX_HDMI_PHY_CONFIG0, ++ TX_HDMI_PHY_CONFIG0_HDMI_COMMON_B7_B0); ++ ++ regmap_write(priv->regmap, TX_HDCP_MODE, 0x40); ++ ++ /* ++ * The vendor driver comments that this is a setting for "Band-gap and ++ * main-bias". 0x1d = power-up, 0x00 = power-down. ++ */ ++ regmap_write(priv->regmap, TX_SYS1_AFE_TEST, 0x1d); ++ ++ meson_txc_hdmi_config_serializer_clock(priv, HDMI_COLORIMETRY_NONE); ++ ++ /* ++ * The vendor driver has a comment with the following information for ++ * the magic value: ++ * bit[2:0]=011: CK channel output TMDS CLOCK ++ * bit[2:0]=101, ck channel output PHYCLCK ++ */ ++ regmap_write(priv->regmap, TX_SYS1_AFE_CONNECT, 0xfb); ++ ++ /* Termination resistor calib value */ ++ regmap_write(priv->regmap, TX_CORE_CALIB_VALUE, 0x0f); ++ ++ /* HPD glitch filter */ ++ regmap_write(priv->regmap, TX_HDCP_HPD_FILTER_L, 0xa0); ++ regmap_write(priv->regmap, TX_HDCP_HPD_FILTER_H, 0xa0); ++ ++ /* Disable MEM power-down */ ++ regmap_write(priv->regmap, TX_MEM_PD_REG0, 0x0); ++ ++ regmap_write(priv->regmap, TX_HDCP_CONFIG3, ++ FIELD_PREP(TX_HDCP_CONFIG3_DDC_I2C_BUS_CLOCK_TIME_DIVIDER, ++ (sys_clk_hz / ddc_i2c_bus_clk_hz) - 1)); ++ ++ /* Enable software controlled DDC transaction */ ++ regmap_write(priv->regmap, TX_HDCP_EDID_CONFIG, ++ TX_HDCP_EDID_CONFIG_FORCED_MEM_COPY_DONE | ++ TX_HDCP_EDID_CONFIG_MEM_COPY_DONE_CONFIG); ++ regmap_write(priv->regmap, TX_CORE_EDID_CONFIG_MORE, ++ TX_CORE_EDID_CONFIG_MORE_SYS_TRIGGER_CONFIG_SEMI_MANU); ++ ++ /* mask (= disable) all interrupts */ ++ regmap_write(priv->regmap, HDMI_OTHER_INTR_MASKN, 0x0); ++ ++ /* clear any pending interrupt */ ++ regmap_write(priv->regmap, HDMI_OTHER_INTR_STAT_CLR, ++ HDMI_OTHER_INTR_STAT_CLR_EDID_RISING | ++ HDMI_OTHER_INTR_STAT_CLR_HPD_FALLING | ++ HDMI_OTHER_INTR_STAT_CLR_HPD_RISING); ++ ++ return 0; ++ ++err_phy_exit: ++ phy_exit(priv->phy); ++ return 0; ++} ++ ++static void meson_txc_hdmi_hw_exit(struct meson_txc_hdmi *priv) ++{ ++ int ret; ++ ++ /* mask (= disable) all interrupts */ ++ regmap_write(priv->regmap, HDMI_OTHER_INTR_MASKN, ++ HDMI_OTHER_INTR_MASKN_TX_EDID_INT_RISE | ++ HDMI_OTHER_INTR_MASKN_TX_HPD_INT_FALL | ++ HDMI_OTHER_INTR_MASKN_TX_HPD_INT_RISE); ++ ++ regmap_update_bits(priv->regmap, HDMI_OTHER_CTRL1, ++ HDMI_OTHER_CTRL1_POWER_ON, 0); ++ ++ clk_disable_unprepare(priv->sys_clk); ++ ++ ret = phy_exit(priv->phy); ++ if (ret) ++ dev_err(priv->dev, "Failed to exit the PHY: %d\n", ret); ++} ++ ++static u32 meson_txc_hdmi_hdmi_codec_calc_audio_n(struct hdmi_codec_params *hparms) ++{ ++ u32 audio_n; ++ ++ if ((hparms->sample_rate % 44100) == 0) ++ audio_n = (128 * hparms->sample_rate) / 900; ++ else ++ audio_n = (128 * hparms->sample_rate) / 1000; ++ ++ if (hparms->cea.coding_type == HDMI_AUDIO_CODING_TYPE_EAC3 || ++ hparms->cea.coding_type == HDMI_AUDIO_CODING_TYPE_DTS_HD) ++ audio_n *= 4; ++ ++ return audio_n; ++} ++ ++static u8 meson_txc_hdmi_hdmi_codec_coding_type(struct hdmi_codec_params *hparms) ++{ ++ switch (hparms->cea.coding_type) { ++ case HDMI_AUDIO_CODING_TYPE_MLP: ++ return TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_HBR_AUDIO_PACKET; ++ case HDMI_AUDIO_CODING_TYPE_DSD: ++ return TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_ONE_BIT_AUDIO; ++ case HDMI_AUDIO_CODING_TYPE_DST: ++ return TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_DST_AUDIO_PACKET; ++ default: ++ return TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_AUDIO_SAMPLE_PACKET; ++ } ++} ++ ++static int meson_txc_hdmi_hdmi_codec_hw_params(struct device *dev, void *data, ++ struct hdmi_codec_daifmt *fmt, ++ struct hdmi_codec_params *hparms) ++{ ++ u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)]; ++ struct meson_txc_hdmi *priv = data; ++ u16 audio_tx_format; ++ u32 audio_n; ++ int len, i; ++ ++ if (hparms->cea.coding_type == HDMI_AUDIO_CODING_TYPE_MLP) { ++ /* ++ * TODO: fixed CTS is not supported yet, it needs special ++ * TX_SYS1_ACR_N_* settings ++ */ ++ return -EINVAL; ++ } ++ ++ switch (hparms->sample_width) { ++ case 16: ++ audio_tx_format = FIELD_PREP(TX_AUDIO_FORMAT_BIT_WIDTH_MASK, ++ TX_AUDIO_FORMAT_BIT_WIDTH_16); ++ break; ++ ++ case 20: ++ audio_tx_format = FIELD_PREP(TX_AUDIO_FORMAT_BIT_WIDTH_MASK, ++ TX_AUDIO_FORMAT_BIT_WIDTH_20); ++ break; ++ ++ case 24: ++ audio_tx_format = FIELD_PREP(TX_AUDIO_FORMAT_BIT_WIDTH_MASK, ++ TX_AUDIO_FORMAT_BIT_WIDTH_24); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt->fmt) { ++ case HDMI_I2S: ++ regmap_update_bits(priv->regmap, HDMI_OTHER_CTRL1, ++ HDMI_OTHER_CTRL1_HDMI_AUDIO_CLOCK_ON, ++ HDMI_OTHER_CTRL1_HDMI_AUDIO_CLOCK_ON); ++ ++ audio_tx_format |= TX_AUDIO_FORMAT_SPDIF_OR_I2S | ++ TX_AUDIO_FORMAT_I2S_ONE_BIT_OR_I2S | ++ FIELD_PREP(TX_AUDIO_FORMAT_I2S_FORMAT, 0x2); ++ ++ if (hparms->channels > 2) ++ audio_tx_format |= TX_AUDIO_FORMAT_I2S_2_OR_8_CH; ++ ++ regmap_write(priv->regmap, TX_AUDIO_FORMAT, ++ audio_tx_format); ++ ++ regmap_write(priv->regmap, TX_AUDIO_I2S, TX_AUDIO_I2S_ENABLE); ++ regmap_write(priv->regmap, TX_AUDIO_SPDIF, 0x0); ++ break; ++ ++ case HDMI_SPDIF: ++ regmap_update_bits(priv->regmap, HDMI_OTHER_CTRL1, ++ HDMI_OTHER_CTRL1_HDMI_AUDIO_CLOCK_ON, 0x0); ++ ++ if (hparms->cea.coding_type == HDMI_AUDIO_CODING_TYPE_STREAM) ++ audio_tx_format |= TX_AUDIO_FORMAT_SPDIF_CHANNEL_STATUS_FROM_DATA_OR_REG; ++ ++ regmap_write(priv->regmap, TX_AUDIO_FORMAT, ++ audio_tx_format); ++ ++ regmap_write(priv->regmap, TX_AUDIO_I2S, 0x0); ++ regmap_write(priv->regmap, TX_AUDIO_SPDIF, TX_AUDIO_SPDIF_ENABLE); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ if (hparms->channels > 2) ++ regmap_write(priv->regmap, TX_AUDIO_HEADER, ++ TX_AUDIO_HEADER_AUDIO_SAMPLE_PACKET_HEADER_LAYOUT1); ++ else ++ regmap_write(priv->regmap, TX_AUDIO_HEADER, 0x0); ++ ++ regmap_write(priv->regmap, TX_AUDIO_SAMPLE, ++ FIELD_PREP(TX_AUDIO_SAMPLE_CHANNEL_VALID, ++ BIT(hparms->channels) - 1)); ++ ++ audio_n = meson_txc_hdmi_hdmi_codec_calc_audio_n(hparms); ++ ++ regmap_write(priv->regmap, TX_SYS1_ACR_N_0, ++ FIELD_PREP(TX_SYS1_ACR_N_0_N_BYTE0, ++ (audio_n >> 0) & 0xff)); ++ regmap_write(priv->regmap, TX_SYS1_ACR_N_1, ++ FIELD_PREP(TX_SYS1_ACR_N_1_N_BYTE1, ++ (audio_n >> 8) & 0xff)); ++ regmap_update_bits(priv->regmap, TX_SYS1_ACR_N_2, ++ TX_SYS1_ACR_N_2_N_UPPER_NIBBLE, ++ FIELD_PREP(TX_SYS1_ACR_N_2_N_UPPER_NIBBLE, ++ (audio_n >> 16) & 0xf)); ++ ++ regmap_write(priv->regmap, TX_SYS0_ACR_CTS_0, 0x0); ++ regmap_write(priv->regmap, TX_SYS0_ACR_CTS_1, 0x0); ++ regmap_write(priv->regmap, TX_SYS0_ACR_CTS_2, ++ TX_SYS0_ACR_CTS_2_FORCE_ARC_STABLE); ++ ++ regmap_write(priv->regmap, TX_AUDIO_CONTROL, ++ TX_AUDIO_CONTROL_AUTO_AUDIO_FIFO_CLEAR | ++ FIELD_PREP(TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_MASK, ++ meson_txc_hdmi_hdmi_codec_coding_type(hparms)) | ++ TX_AUDIO_CONTROL_AUDIO_SAMPLE_PACKET_FLAT); ++ ++ len = hdmi_audio_infoframe_pack(&hparms->cea, buf, sizeof(buf)); ++ if (len < 0) ++ return len; ++ ++ meson_txc_hdmi_write_infoframe(priv->regmap, ++ TX_PKT_REG_AUDIO_INFO_BASE_ADDR, ++ buf, len, true); ++ ++ for (i = 0; i < ARRAY_SIZE(hparms->iec.status); i++) { ++ unsigned char sub1, sub2; ++ ++ sub1 = sub2 = hparms->iec.status[i]; ++ ++ if (i == 2) { ++ sub1 |= FIELD_PREP(IEC958_AES2_CON_CHANNEL, 1); ++ sub2 |= FIELD_PREP(IEC958_AES2_CON_CHANNEL, 2); ++ } ++ ++ regmap_write(priv->regmap, TX_IEC60958_SUB1_OFFSET + i, sub1); ++ regmap_write(priv->regmap, TX_IEC60958_SUB2_OFFSET + i, sub2); ++ } ++ ++ return 0; ++} ++ ++static int meson_txc_hdmi_hdmi_codec_audio_startup(struct device *dev, ++ void *data) ++{ ++ struct meson_txc_hdmi *priv = data; ++ ++ regmap_update_bits(priv->regmap, TX_PACKET_CONTROL_2, ++ TX_PACKET_CONTROL_2_AUDIO_REQUEST_DISABLE, 0x0); ++ ++ /* reset audio master and sample */ ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, ++ TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_RESAMPLE_RSTN | ++ TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_MASTER_RSTN); ++ regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, 0x0); ++ ++ regmap_write(priv->regmap, TX_AUDIO_CONTROL_MORE, ++ TX_AUDIO_CONTROL_MORE_ENABLE); ++ ++ regmap_write(priv->regmap, TX_AUDIO_FIFO, ++ FIELD_PREP(TX_AUDIO_FIFO_FIFO_DEPTH_MASK, ++ TX_AUDIO_FIFO_FIFO_DEPTH_512) | ++ FIELD_PREP(TX_AUDIO_FIFO_CRITICAL_THRESHOLD_MASK, ++ TX_AUDIO_FIFO_CRITICAL_THRESHOLD_DEPTH_DIV16) | ++ FIELD_PREP(TX_AUDIO_FIFO_NORMAL_THRESHOLD_MASK, ++ TX_AUDIO_FIFO_NORMAL_THRESHOLD_DEPTH_DIV8)); ++ ++ regmap_write(priv->regmap, TX_AUDIO_LIPSYNC, 0x0); ++ ++ regmap_write(priv->regmap, TX_SYS1_ACR_N_2, ++ FIELD_PREP(TX_SYS1_ACR_N_2_N_MEAS_TOLERANCE, 0x3)); ++ ++ return 0; ++} ++ ++static void meson_txc_hdmi_hdmi_codec_audio_shutdown(struct device *dev, ++ void *data) ++{ ++ struct meson_txc_hdmi *priv = data; ++ ++ meson_txc_hdmi_disable_infoframe(priv, TX_PKT_REG_AUDIO_INFO_BASE_ADDR); ++ ++ regmap_write(priv->regmap, TX_AUDIO_CONTROL_MORE, 0x0); ++ regmap_update_bits(priv->regmap, HDMI_OTHER_CTRL1, ++ HDMI_OTHER_CTRL1_HDMI_AUDIO_CLOCK_ON, 0x0); ++ ++ regmap_update_bits(priv->regmap, TX_PACKET_CONTROL_2, ++ TX_PACKET_CONTROL_2_AUDIO_REQUEST_DISABLE, ++ TX_PACKET_CONTROL_2_AUDIO_REQUEST_DISABLE); ++} ++ ++static int meson_txc_hdmi_hdmi_codec_mute_stream(struct device *dev, ++ void *data, ++ bool enable, int direction) ++{ ++ struct meson_txc_hdmi *priv = data; ++ ++ regmap_write(priv->regmap, TX_AUDIO_PACK, ++ enable ? 0 : TX_AUDIO_PACK_AUDIO_SAMPLE_PACKETS_ENABLE); ++ ++ return 0; ++} ++ ++static int meson_txc_hdmi_hdmi_codec_get_eld(struct device *dev, void *data, ++ uint8_t *buf, size_t len) ++{ ++ struct meson_txc_hdmi *priv = data; ++ ++ if (priv->current_connector) ++ memcpy(buf, priv->current_connector->eld, ++ min_t(size_t, MAX_ELD_BYTES, len)); ++ else ++ memset(buf, 0, len); ++ ++ return 0; ++} ++ ++static int meson_txc_hdmi_hdmi_codec_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 meson_txc_hdmi_hdmi_codec_hook_plugged_cb(struct device *dev, ++ void *data, ++ hdmi_codec_plugged_cb fn, ++ struct device *codec_dev) ++{ ++ struct meson_txc_hdmi *priv = data; ++ ++ mutex_lock(&priv->codec_mutex); ++ priv->codec_plugged_cb = fn; ++ priv->codec_dev = codec_dev; ++ meson_txc_hdmi_handle_plugged_change(priv); ++ mutex_unlock(&priv->codec_mutex); ++ ++ return 0; ++} ++ ++static struct hdmi_codec_ops meson_txc_hdmi_hdmi_codec_ops = { ++ .hw_params = meson_txc_hdmi_hdmi_codec_hw_params, ++ .audio_startup = meson_txc_hdmi_hdmi_codec_audio_startup, ++ .audio_shutdown = meson_txc_hdmi_hdmi_codec_audio_shutdown, ++ .mute_stream = meson_txc_hdmi_hdmi_codec_mute_stream, ++ .get_eld = meson_txc_hdmi_hdmi_codec_get_eld, ++ .get_dai_id = meson_txc_hdmi_hdmi_codec_get_dai_id, ++ .hook_plugged_cb = meson_txc_hdmi_hdmi_codec_hook_plugged_cb, ++}; ++ ++static int meson_txc_hdmi_hdmi_codec_init(struct meson_txc_hdmi *priv) ++{ ++ struct hdmi_codec_pdata pdata = { ++ .ops = &meson_txc_hdmi_hdmi_codec_ops, ++ .i2s = 1, ++ .spdif = 1, ++ .max_i2s_channels = 8, ++ .data = priv, ++ }; ++ ++ priv->hdmi_codec_pdev = platform_device_register_data(priv->dev, ++ HDMI_CODEC_DRV_NAME, ++ PLATFORM_DEVID_AUTO, ++ &pdata, sizeof(pdata)); ++ return PTR_ERR_OR_ZERO(priv->hdmi_codec_pdev); ++} ++ ++static int meson_txc_hdmi_bind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct meson_txc_hdmi *priv; ++ void __iomem *base; ++ u32 regval; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->dev = dev; ++ ++ mutex_init(&priv->codec_mutex); ++ ++ dev_set_drvdata(dev, priv); ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ priv->regmap = devm_regmap_init(dev, NULL, base, ++ &meson_txc_hdmi_regmap_config); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ ++ priv->pclk = devm_clk_get(dev, "pclk"); ++ if (IS_ERR(priv->pclk)) { ++ ret = PTR_ERR(priv->pclk); ++ return dev_err_probe(dev, ret, "Failed to get the pclk\n"); ++ } ++ ++ priv->sys_clk = devm_clk_get(dev, "sys"); ++ if (IS_ERR(priv->sys_clk)) { ++ ret = PTR_ERR(priv->sys_clk); ++ return dev_err_probe(dev, ret, ++ "Failed to get the sys clock\n"); ++ } ++ ++ priv->phy = devm_phy_get(dev, "hdmi"); ++ if (IS_ERR(priv->phy)) { ++ ret = PTR_ERR(priv->phy); ++ return dev_err_probe(dev, ret, "Failed to get the HDMI PHY\n"); ++ } ++ ++ ret = meson_txc_hdmi_parse_dt(priv); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(priv->pclk); ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to enable the pclk\n"); ++ return ret; ++ } ++ ++ regval = readl(base + HDMI_CTRL_PORT); ++ regval |= HDMI_CTRL_PORT_APB3_ERR_EN; ++ writel(regval, base + HDMI_CTRL_PORT); ++ ++ ret = meson_txc_hdmi_hw_init(priv); ++ if (ret) ++ goto err_disable_clk; ++ ++ ret = meson_txc_hdmi_hdmi_codec_init(priv); ++ if (ret) ++ goto err_hw_exit; ++ ++ priv->bridge.driver_private = priv; ++ priv->bridge.funcs = &meson_txc_hdmi_bridge_funcs; ++ priv->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; ++ priv->bridge.of_node = dev->of_node; ++ priv->bridge.interlace_allowed = true; ++ ++ drm_bridge_add(&priv->bridge); ++ ++ return 0; ++ ++err_hw_exit: ++ meson_txc_hdmi_hw_exit(priv); ++err_disable_clk: ++ clk_disable_unprepare(priv->pclk); ++ return ret; ++} ++ ++static void meson_txc_hdmi_unbind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct meson_txc_hdmi *priv = dev_get_drvdata(dev); ++ ++ platform_device_unregister(priv->hdmi_codec_pdev); ++ ++ drm_bridge_remove(&priv->bridge); ++ ++ meson_txc_hdmi_hw_exit(priv); ++ ++ clk_disable_unprepare(priv->pclk); ++} ++ ++static const struct component_ops meson_txc_hdmi_component_ops = { ++ .bind = meson_txc_hdmi_bind, ++ .unbind = meson_txc_hdmi_unbind, ++}; ++ ++static int meson_txc_hdmi_probe(struct platform_device *pdev) ++{ ++ return component_add(&pdev->dev, &meson_txc_hdmi_component_ops); ++} ++ ++static int meson_txc_hdmi_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &meson_txc_hdmi_component_ops); ++ ++ return 0; ++} ++ ++static const struct of_device_id meson_txc_hdmi_of_table[] = { ++ { .compatible = "amlogic,meson8-hdmi-tx" }, ++ { .compatible = "amlogic,meson8b-hdmi-tx" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, meson_txc_hdmi_of_table); ++ ++static struct platform_driver meson_txc_hdmi_platform_driver = { ++ .probe = meson_txc_hdmi_probe, ++ .remove = meson_txc_hdmi_remove, ++ .driver = { ++ .name = "meson-transwitch-hdmi", ++ .of_match_table = meson_txc_hdmi_of_table, ++ }, ++}; ++module_platform_driver(meson_txc_hdmi_platform_driver); ++ ++MODULE_AUTHOR("Martin Blumenstingl "); ++MODULE_DESCRIPTION("Amlogic Meson8 and Meson8b TranSwitch HDMI 1.4 TX driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/gpu/drm/meson/meson_transwitch_hdmi.h b/drivers/gpu/drm/meson/meson_transwitch_hdmi.h +new file mode 100644 +index 00000000..14929475 +--- /dev/null ++++ b/drivers/gpu/drm/meson/meson_transwitch_hdmi.h +@@ -0,0 +1,536 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2021 Martin Blumenstingl ++ * ++ * All registers and magic values are taken from Amlogic's GPL kernel sources: ++ * Copyright (C) 2010 Amlogic, Inc. ++ */ ++ ++#include ++#include ++ ++#ifndef __MESON_TRANSWITCH_HDMI_H__ ++#define __MESON_TRANSWITCH_HDMI_H__ ++ ++/* HDMI TX register */ ++ ++// System config 0 ++#define TX_SYS0_AFE_SIGNAL 0x0000 ++#define TX_SYS0_AFE_LOOP 0x0001 ++#define TX_SYS0_ACR_CTS_0 0x0002 ++ #define TX_SYS0_ACR_CTS_0_AUDIO_CTS_BYTE0 GENMASK(7, 0) ++#define TX_SYS0_ACR_CTS_1 0x0003 ++ #define TX_SYS0_ACR_CTS_1_AUDIO_CTS_BYTE1 GENMASK(7, 0) ++#define TX_SYS0_ACR_CTS_2 0x0004 ++ #define TX_SYS0_ACR_CTS_2_FORCE_ARC_STABLE BIT(5) ++#define TX_SYS0_BIST_CONTROL 0x0005 ++ #define TX_SYS0_BIST_CONTROL_AFE_BIST_ENABLE BIT(7) ++ #define TX_SYS0_BIST_CONTROL_TMDS_SHIFT_PATTERN_SELECT BIT(6) ++ #define TX_SYS0_BIST_CONTROL_TMDS_PRBS_PATTERN_SELECT GENMASK(5, 4) ++ #define TX_SYS0_BIST_CONTROL_TMDS_REPEAT_BIST_PATTERN GENMASK(2, 0) ++ ++#define TX_SYS0_BIST_DATA_0 0x0006 ++#define TX_SYS0_BIST_DATA_1 0x0007 ++#define TX_SYS0_BIST_DATA_2 0x0008 ++#define TX_SYS0_BIST_DATA_3 0x0009 ++#define TX_SYS0_BIST_DATA_4 0x000A ++#define TX_SYS0_BIST_DATA_5 0x000B ++#define TX_SYS0_BIST_DATA_6 0x000C ++#define TX_SYS0_BIST_DATA_7 0x000D ++#define TX_SYS0_BIST_DATA_8 0x000E ++#define TX_SYS0_BIST_DATA_9 0x000F ++ ++// system config 1 ++#define TX_HDMI_PHY_CONFIG0 0x0010 ++ #define TX_HDMI_PHY_CONFIG0_HDMI_COMMON_B7_B0 GENMASK(7, 0) ++#define TX_HDMI_PHY_CONFIG1 0x0010 ++ #define TX_HDMI_PHY_CONFIG1_HDMI_COMMON_B11_B8 GENMASK(3, 0) ++ #define TX_HDMI_PHY_CONFIG1_HDMI_CTL_REG_B3_B0 GENMASK(7, 4) ++#define TX_HDMI_PHY_CONFIG2 0x0012 ++ #define TX_HDMI_PHY_CONFIG_HDMI_CTL_REG_B11_B4 GENMASK(7, 0) ++#define TX_HDMI_PHY_CONFIG3 0x0013 ++ #define TX_HDMI_PHY_CONFIG3_HDMI_L2H_CTL GENMASK(3, 0) ++ #define TX_HDMI_PHY_CONFIG3_HDMI_MDR_PU GENMASK(7, 4) ++#define TX_HDMI_PHY_CONFIG4 0x0014 ++ #define TX_HDMI_PHY_CONFIG4_HDMI_LF_PD BIT(0) ++ #define TX_HDMI_PHY_CONFIG4_HDMI_PHY_CLK_EN BIT(1) ++ #define TX_HDMI_PHY_CONFIG4_HDMI_MODE GENMASK(3, 2) ++ #define TX_HDMI_PHY_CONFIG4_HDMI_MODE_NORMAL 0x0 ++ #define TX_HDMI_PHY_CONFIG4_HDMI_MODE_CLK_CH3_EQUAL_CH0 0x1 ++ #define TX_HDMI_PHY_CONFIG4_HDMI_MODE_ALTERNATE_HIGH_LOW 0x2 ++ #define TX_HDMI_PHY_CONFIG4_HDMI_MODE_ALTERNATE_LOW_HIGH 0x3 ++ #define TX_HDMI_PHY_CONFIG4_HDMI_PREM_CTL GENMASK(7, 4) ++#define TX_HDMI_PHY_CONFIG5 0x0015 ++ #define TX_HDMI_PHY_CONFIG5_HDMI_VCM_CTL GENMASK(7, 5) ++ #define TX_HDMI_PHY_CONFIG5_HDMI_PREFCTL GENMASK(2, 0) ++#define TX_HDMI_PHY_CONFIG6 0x0016 ++ #define TX_HDMI_PHY_CONFIG6_HDMI_RTERM_CTL GENMASK(3, 0) ++ #define TX_HDMI_PHY_CONFIG6_HDMI_SWING_CTL GENMASK(7, 4) ++#define TX_SYS1_AFE_TEST 0x0017 ++#define TX_SYS1_PLL 0x0018 ++#define TX_SYS1_TUNE 0x0019 ++#define TX_SYS1_AFE_CONNECT 0x001A ++ ++#define TX_SYS1_ACR_N_0 0x001C ++ #define TX_SYS1_ACR_N_0_N_BYTE0 GENMASK(7, 0) ++#define TX_SYS1_ACR_N_1 0x001D ++ #define TX_SYS1_ACR_N_1_N_BYTE1 GENMASK(7, 0) ++#define TX_SYS1_ACR_N_2 0x001E ++ #define TX_SYS1_ACR_N_2_N_MEAS_TOLERANCE GENMASK(7, 4) ++ #define TX_SYS1_ACR_N_2_N_UPPER_NIBBLE GENMASK(3, 0) ++#define TX_SYS1_PRBS_DATA 0x001F ++ #define TX_SYS1_PRBS_DATA_PRBS_MODE GENMASK(1, 0) ++ #define TX_SYS1_PRBS_DATA_PRBS_MODE_11 0x0 ++ #define TX_SYS1_PRBS_DATA_PRBS_MODE_15 0x1 ++ #define TX_SYS1_PRBS_DATA_PRBS_MODE_7 0x2 ++ #define TX_SYS1_PRBS_DATA_PRBS_MODE_31 0x3 ++ ++// HDCP CONFIG ++#define TX_HDCP_ECC_CONFIG 0x0024 ++#define TX_HDCP_CRC_CONFIG 0x0025 ++#define TX_HDCP_EDID_CONFIG 0x0026 ++ #define TX_HDCP_EDID_CONFIG_FORCED_SYS_TRIGGER BIT(7) ++ #define TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG BIT(6) ++ #define TX_HDCP_EDID_CONFIG_MEM_ACC_SEQ_MODE BIT(5) ++ #define TX_HDCP_EDID_CONFIG_MEM_ACC_SEQ_START BIT(4) ++ #define TX_HDCP_EDID_CONFIG_FORCED_MEM_COPY_DONE BIT(3) ++ #define TX_HDCP_EDID_CONFIG_MEM_COPY_DONE_CONFIG BIT(2) ++ #define TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG_SEMI_MANU BIT(1) ++ ++#define TX_HDCP_MEM_CONFIG 0x0027 ++ #define TX_HDCP_MEM_CONFIG_READ_DECRYPT BIT(3) ++ ++#define TX_HDCP_HPD_FILTER_L 0x0028 ++#define TX_HDCP_HPD_FILTER_H 0x0029 ++#define TX_HDCP_ENCRYPT_BYTE 0x002A ++#define TX_HDCP_CONFIG0 0x002B ++ #define TX_HDCP_CONFIG0_ROM_ENCRYPT_OFF GENMASK(4, 3) ++ ++#define TX_HDCP_CONFIG1 0x002C ++#define TX_HDCP_CONFIG2 0x002D ++#define TX_HDCP_CONFIG3 0x002E ++ #define TX_HDCP_CONFIG3_DDC_I2C_BUS_CLOCK_TIME_DIVIDER GENMASK(7, 0) ++ ++#define TX_HDCP_MODE 0x002F ++ #define TX_HDCP_MODE_CP_DESIRED BIT(7) ++ #define TX_HDCP_MODE_ESS_CONFIG BIT(6) ++ #define TX_HDCP_MODE_SET_AVMUTE BIT(5) ++ #define TX_HDCP_MODE_CLEAR_AVMUTE BIT(4) ++ #define TX_HDCP_MODE_HDCP_1_1 BIT(3) ++ #define TX_HDCP_MODE_VSYNC_HSYNC_FORCED_POLARITY_SELECT BIT(2) ++ #define TX_HDCP_MODE_FORCED_VSYNC_POLARITY BIT(1) ++ #define TX_HDCP_MODE_FORCED_HSYNC_POLARITY BIT(0) ++ ++// Video config, part 1 ++#define TX_VIDEO_ACTIVE_PIXELS_0 0x0030 ++#define TX_VIDEO_ACTIVE_PIXELS_1 0x0031 ++#define TX_VIDEO_FRONT_PIXELS 0x0032 ++#define TX_VIDEO_HSYNC_PIXELS 0x0033 ++#define TX_VIDEO_BACK_PIXELS 0x0034 ++#define TX_VIDEO_ACTIVE_LINES_0 0x0035 ++#define TX_VIDEO_ACTIVE_LINES_1 0x0036 ++#define TX_VIDEO_EOF_LINES 0x0037 ++#define TX_VIDEO_VSYNC_LINES 0x0038 ++#define TX_VIDEO_SOF_LINES 0x0039 ++#define TX_VIDEO_DTV_TIMING 0x003A ++ #define TX_VIDEO_DTV_TIMING_FORCE_DTV_TIMING_AUTO BIT(7) ++ #define TX_VIDEO_DTV_TIMING_FORCE_VIDEO_SCAN BIT(6) ++ #define TX_VIDEO_DTV_TIMING_FORCE_VIDEO_FIELD BIT(5) ++ #define TX_VIDEO_DTV_TIMING_DISABLE_VIC39_CORRECTION BIT(4) ++ ++#define TX_VIDEO_DTV_MODE 0x003B ++ #define TX_VIDEO_DTV_MODE_FORCED_DEFAULT_PHASE BIT(7) ++ #define TX_VIDEO_DTV_MODE_COLOR_DEPTH GENMASK(1, 0) ++ ++#define TX_VIDEO_DTV_FORMAT0 0x003C ++#define TX_VIDEO_DTV_FORMAT1 0x003D ++#define TX_VIDEO_PIXEL_PACK 0x003F ++// video config, part 2 ++#define TX_VIDEO_CSC_COEFF_B0 0x0040 ++#define TX_VIDEO_CSC_COEFF_B1 0x0041 ++#define TX_VIDEO_CSC_COEFF_R0 0x0042 ++#define TX_VIDEO_CSC_COEFF_R1 0x0043 ++#define TX_VIDEO_CSC_COEFF_CB0 0x0044 ++#define TX_VIDEO_CSC_COEFF_CB1 0x0045 ++#define TX_VIDEO_CSC_COEFF_CR0 0x0046 ++#define TX_VIDEO_CSC_COEFF_CR1 0x0047 ++#define TX_VIDEO_DTV_OPTION_L 0x0048 ++ #define TX_VIDEO_DTV_OPTION_L_OUTPUT_COLOR_FORMAT GENMASK(7, 6) ++ #define TX_VIDEO_DTV_OPTION_L_INPUT_COLOR_FORMAT GENMASK(5, 4) ++ #define TX_VIDEO_DTV_OPTION_L_OUTPUT_COLOR_DEPTH GENMASK(3, 2) ++ #define TX_VIDEO_DTV_OPTION_L_INPUT_COLOR_DEPTH GENMASK(1, 0) ++ ++#define TX_VIDEO_DTV_OPTION_H 0x0049 ++ #define TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_16_235 0x0 ++ #define TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_16_240 0x1 ++ #define TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_1_254 0x2 ++ #define TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_0_255 0x3 ++ #define TX_VIDEO_DTV_OPTION_H_OUTPUT_COLOR_RANGE GENMASK(3, 2) ++ #define TX_VIDEO_DTV_OPTION_H_INPUT_COLOR_RANGE GENMASK(1, 0) ++ ++#define TX_VIDEO_DTV_FILTER 0x004A ++#define TX_VIDEO_DTV_DITHER 0x004B ++#define TX_VIDEO_DTV_DEDITHER 0x004C ++#define TX_VIDEO_PROC_CONFIG0 0x004E ++#define TX_VIDEO_PROC_CONFIG1 0x004F ++ ++// Audio config ++#define TX_AUDIO_FORMAT 0x0058 ++ #define TX_AUDIO_FORMAT_SPDIF_OR_I2S BIT(7) ++ #define TX_AUDIO_FORMAT_I2S_2_OR_8_CH BIT(6) ++ #define TX_AUDIO_FORMAT_I2S_FORMAT GENMASK(5, 4) ++ #define TX_AUDIO_FORMAT_BIT_WIDTH_MASK GENMASK(3, 2) ++ #define TX_AUDIO_FORMAT_BIT_WIDTH_16 0x1 ++ #define TX_AUDIO_FORMAT_BIT_WIDTH_20 0x2 ++ #define TX_AUDIO_FORMAT_BIT_WIDTH_24 0x3 ++ #define TX_AUDIO_FORMAT_WS_POLARITY BIT(1) ++ #define TX_AUDIO_FORMAT_I2S_ONE_BIT_OR_I2S BIT(0) ++ #define TX_AUDIO_FORMAT_SPDIF_CHANNEL_STATUS_FROM_DATA_OR_REG BIT(0) ++ ++#define TX_AUDIO_SPDIF 0x0059 ++ #define TX_AUDIO_SPDIF_ENABLE BIT(0) ++#define TX_AUDIO_I2S 0x005A ++ #define TX_AUDIO_I2S_ENABLE BIT(0) ++#define TX_AUDIO_FIFO 0x005B ++ #define TX_AUDIO_FIFO_FIFO_DEPTH_MASK GENMASK(7, 4) ++ #define TX_AUDIO_FIFO_FIFO_DEPTH_512 0x4 ++ #define TX_AUDIO_FIFO_CRITICAL_THRESHOLD_MASK GENMASK(3, 2) ++ #define TX_AUDIO_FIFO_CRITICAL_THRESHOLD_DEPTH_DIV16 0x2 ++ #define TX_AUDIO_FIFO_NORMAL_THRESHOLD_MASK GENMASK(1, 0) ++ #define TX_AUDIO_FIFO_NORMAL_THRESHOLD_DEPTH_DIV8 0x1 ++#define TX_AUDIO_LIPSYNC 0x005C ++ #define TX_AUDIO_LIPSYNC_NORMALIZED_LIPSYNC_PARAM GENMASK(7, 0) ++#define TX_AUDIO_CONTROL 0x005D ++ #define TX_AUDIO_CONTROL_FORCED_AUDIO_FIFO_CLEAR BIT(7) ++ #define TX_AUDIO_CONTROL_AUTO_AUDIO_FIFO_CLEAR BIT(6) ++ #define TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_MASK GENMASK(5, 4) ++ #define TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_AUDIO_SAMPLE_PACKET 0x0 ++ #define TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_ONE_BIT_AUDIO 0x1 ++ #define TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_HBR_AUDIO_PACKET 0x2 ++ #define TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_DST_AUDIO_PACKET 0x3 ++ #define TX_AUDIO_CONTROL_AUDIO_SAMPLE_PACKET_VALID BIT(2) ++ #define TX_AUDIO_CONTROL_AUDIO_SAMPLE_PACKET_USER BIT(1) ++ #define TX_AUDIO_CONTROL_AUDIO_SAMPLE_PACKET_FLAT BIT(0) ++#define TX_AUDIO_HEADER 0x005E ++ #define TX_AUDIO_HEADER_AUDIO_SAMPLE_PACKET_HEADER_LAYOUT1 BIT(7) ++ #define TX_AUDIO_HEADER_SET_NORMAL_DOUBLE_IN_DST_PACKET_HEADER BIT(6) ++#define TX_AUDIO_SAMPLE 0x005F ++ #define TX_AUDIO_SAMPLE_CHANNEL_VALID GENMASK(7, 0) ++#define TX_AUDIO_VALID 0x0060 ++#define TX_AUDIO_USER 0x0061 ++#define TX_AUDIO_PACK 0x0062 ++ #define TX_AUDIO_PACK_AUDIO_SAMPLE_PACKETS_ENABLE BIT(0) ++#define TX_AUDIO_CONTROL_MORE 0x0064 ++ #define TX_AUDIO_CONTROL_MORE_ENABLE BIT(0) ++ ++// tmds config ++#define TX_TMDS_MODE 0x0068 ++ #define TX_TMDS_MODE_FORCED_HDMI BIT(7) ++ #define TX_TMDS_MODE_HDMI_CONFIG BIT(6) ++ #define TX_TMDS_MODE_BIT_SWAP BIT(3) ++ #define TX_TMDS_MODE_CHANNEL_SWAP GENMASK(2, 0) ++ ++#define TX_TMDS_CONFIG0 0x006C ++#define TX_TMDS_CONFIG1 0x006D ++ ++// packet config ++#define TX_PACKET_ALLOC_ACTIVE_1 0x0078 ++#define TX_PACKET_ALLOC_ACTIVE_2 0x0079 ++#define TX_PACKET_ALLOC_EOF_1 0x007A ++#define TX_PACKET_ALLOC_EOF_2 0x007B ++#define TX_PACKET_ALLOC_SOF_1 0x007C ++#define TX_PACKET_ALLOC_SOF_2 0x007D ++#define TX_PACKET_CONTROL_1 0x007E ++ #define TX_PACKET_CONTROL_1_FORCE_PACKET_TIMING BIT(7) ++ #define TX_PACKET_CONTROL_1_PACKET_ALLOC_MODE BIT(6) ++ #define TX_PACKET_CONTROL_1_PACKET_START_LATENCY GENMASK(5, 0) ++ ++#define TX_PACKET_CONTROL_2 0x007F ++ #define TX_PACKET_CONTROL_2_AUDIO_REQUEST_DISABLE BIT(3) ++ #define TX_PACKET_CONTROL_2_HORIZONTAL_GC_PACKET_TRANSPORT_EN BIT(1) ++ ++#define TX_CORE_EDID_CONFIG_MORE 0x0080 ++ #define TX_CORE_EDID_CONFIG_MORE_KEEP_EDID_ERROR BIT(1) ++ #define TX_CORE_EDID_CONFIG_MORE_SYS_TRIGGER_CONFIG_SEMI_MANU BIT(0) ++ ++#define TX_CORE_ALLOC_VSYNC_0 0x0081 ++#define TX_CORE_ALLOC_VSYNC_1 0x0082 ++#define TX_CORE_ALLOC_VSYNC_2 0x0083 ++#define TX_MEM_PD_REG0 0x0084 ++ ++// core config ++#define TX_CORE_DATA_CAPTURE_1 0x00F0 ++#define TX_CORE_DATA_CAPTURE_2 0x00F1 ++ #define TX_CORE_DATA_CAPTURE_2_AUDIO_SOURCE_SELECT GENMASK(7, 6) ++ #define TX_CORE_DATA_CAPTURE_2_EXTERNAL_PACKET_ENABLE BIT(5) ++ #define TX_CORE_DATA_CAPTURE_2_INTERNAL_PACKET_ENABLE BIT(4) ++ #define TX_CORE_DATA_CAPTURE_2_AFE_FIFO_SRC_LANE1 GENMASK(3, 2) ++ #define TX_CORE_DATA_CAPTURE_2_AFE_FIFO_SRC_LANE0 GENMASK(1, 0) ++ ++#define TX_CORE_DATA_MONITOR_1 0x00F2 ++ #define TX_CORE_DATA_MONITOR_1_LANE1 BIT(7) ++ #define TX_CORE_DATA_MONITOR_1_SELECT_LANE1 GENMASK(6, 4) ++ #define TX_CORE_DATA_MONITOR_1_LANE0 BIT(3) ++ #define TX_CORE_DATA_MONITOR_1_SELECT_LANE0 GENMASK(2, 0) ++ ++#define TX_CORE_DATA_MONITOR_2 0x00F3 ++ #define TX_CORE_DATA_MONITOR_2_MONITOR_SELECT GENMASK(2, 0) ++ ++#define TX_CORE_CALIB_MODE 0x00F4 ++#define TX_CORE_CALIB_SAMPLE_DELAY 0x00F5 ++#define TX_CORE_CALIB_VALUE_AUTO 0x00F6 ++#define TX_CORE_CALIB_VALUE 0x00F7 ++ ++// system config 4 ++#define TX_SYS4_TX_CKI_DDR 0x00A0 ++#define TX_SYS4_TX_CKO_DDR 0x00A1 ++#define TX_SYS4_RX_CKI_DDR 0x00A2 ++#define TX_SYS4_RX_CKO_DDR 0x00A3 ++#define TX_SYS4_CONNECT_SEL_0 0x00A4 ++#define TX_SYS4_CONNECT_SEL_1 0x00A5 ++ #define TX_SYS4_CONNECT_SEL_1_TX_CONNECT_SEL_UPPER_CHANNEL_DATA BIT(6) ++ ++#define TX_SYS4_CONNECT_SEL_2 0x00A6 ++#define TX_SYS4_CONNECT_SEL_3 0x00A7 ++#define TX_SYS4_CK_INV_VIDEO 0x00A8 ++ #define TX_SYS4_CK_INV_VIDEO_TMDS_CLK_PATTERN BIT(4) ++#define TX_SYS4_CK_INV_AUDIO 0x00A9 ++#define TX_SYS4_CK_INV_AFE 0x00AA ++#define TX_SYS4_CK_INV_CH01 0x00AB ++#define TX_SYS4_CK_INV_CH2 0x00AC ++#define TX_SYS4_CK_CEC 0x00AD ++#define TX_SYS4_CK_SOURCE_1 0x00AE ++#define TX_SYS4_CK_SOURCE_2 0x00AF ++ ++#define TX_IEC60958_SUB1_OFFSET 0x00B0 ++#define TX_IEC60958_SUB2_OFFSET 0x00C8 ++ ++// system config 5 ++#define TX_SYS5_TX_SOFT_RESET_1 0x00E0 ++ #define TX_SYS5_TX_SOFT_RESET_1_TX_PIXEL_RSTN BIT(7) ++ #define TX_SYS5_TX_SOFT_RESET_1_TX_TMDS_RSTN BIT(6) ++ #define TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_MASTER_RSTN BIT(5) ++ #define TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_RESAMPLE_RSTN BIT(4) ++ #define TX_SYS5_TX_SOFT_RESET_1_TX_I2S_RESET_RSTN BIT(3) ++ #define TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH2 BIT(2) ++ #define TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH1 BIT(1) ++ #define TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH0 BIT(0) ++ ++#define TX_SYS5_TX_SOFT_RESET_2 0x00E1 ++ #define TX_SYS5_TX_SOFT_RESET_2_HDMI_CH3_RST_IN BIT(7) ++ #define TX_SYS5_TX_SOFT_RESET_2_HDMI_CH2_RST_IN BIT(6) ++ #define TX_SYS5_TX_SOFT_RESET_2_HDMI_CH1_RST_IN BIT(5) ++ #define TX_SYS5_TX_SOFT_RESET_2_HDMI_CH0_RST_IN BIT(4) ++ #define TX_SYS5_TX_SOFT_RESET_2_HDMI_SR_RST BIT(3) ++ #define TX_SYS5_TX_SOFT_RESET_2_TX_DDC_HDCP_RSTN BIT(2) ++ #define TX_SYS5_TX_SOFT_RESET_2_TX_DDC_EDID_RSTN BIT(1) ++ #define TX_SYS5_TX_SOFT_RESET_2_TX_DIG_RESET_N_CH3 BIT(0) ++ ++#define TX_SYS5_RX_SOFT_RESET_1 0x00E2 ++#define TX_SYS5_RX_SOFT_RESET_2 0x00E3 ++#define TX_SYS5_RX_SOFT_RESET_3 0x00E4 ++#define TX_SYS5_SSTL_BIDIR_IN 0x00E5 ++#define TX_SYS5_SSTL_IN 0x00E6 ++#define TX_SYS5_SSTL_DIFF_IN 0x00E7 ++#define TX_SYS5_FIFO_CONFIG 0x00E8 ++ #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL2_BYPASS BIT(6) ++ #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL1_BYPASS BIT(5) ++ #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL0_BYPASS BIT(4) ++ #define TX_SYS5_FIFO_CONFIG_CLK_CHANNEL3_OUTPUT_ENABLE BIT(3) ++ #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL2_ENABLE BIT(2) ++ #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL1_ENABLE BIT(1) ++ #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL0_ENABLE BIT(0) ++ ++#define TX_SYS5_FIFO_SAMP01_CFG 0x00E9 ++#define TX_SYS5_FIFO_SAMP23_CFG 0x00EA ++#define TX_SYS5_CONNECT_FIFO_CFG 0x00EB ++#define TX_SYS5_IO_CALIB_CONTROL 0x00EC ++#define TX_SYS5_SSTL_BIDIR_OUT 0x00ED ++#define TX_SYS5_SSTL_OUT 0x00EE ++#define TX_SYS5_SSTL_DIFF_OUT 0x00EF ++ ++// HDCP shadow register ++#define TX_HDCP_SHW_BKSV_0 0x0100 ++#define TX_HDCP_SHW_BKSV_1 0x0101 ++#define TX_HDCP_SHW_BKSV_2 0x0102 ++#define TX_HDCP_SHW_BKSV_3 0x0103 ++#define TX_HDCP_SHW_BKSV_4 0x0104 ++#define TX_HDCP_SHW_RI1_0 0x0108 ++#define TX_HDCP_SHW_RI1_1 0x0109 ++#define TX_HDCP_SHW_PJ1 0x010A ++#define TX_HDCP_SHW_AKSV_0 0x0110 ++#define TX_HDCP_SHW_AKSV_1 0x0111 ++#define TX_HDCP_SHW_AKSV_2 0x0112 ++#define TX_HDCP_SHW_AKSV_3 0x0113 ++#define TX_HDCP_SHW_AKSV_4 0x0114 ++#define TX_HDCP_SHW_AINFO 0x0115 ++#define TX_HDCP_SHW_AN_0 0x0118 ++#define TX_HDCP_SHW_AN_1 0x0119 ++#define TX_HDCP_SHW_AN_2 0x011A ++#define TX_HDCP_SHW_AN_3 0x011B ++#define TX_HDCP_SHW_AN_4 0x011C ++#define TX_HDCP_SHW_AN_5 0x011D ++#define TX_HDCP_SHW_AN_6 0x011E ++#define TX_HDCP_SHW_AN_7 0x011F ++#define TX_HDCP_SHW_V1_H0_0 0x0120 ++#define TX_HDCP_SHW_V1_H0_1 0x0121 ++#define TX_HDCP_SHW_V1_H0_2 0x0122 ++#define TX_HDCP_SHW_V1_H0_3 0x0123 ++#define TX_HDCP_SHW_V1_H1_0 0x0124 ++#define TX_HDCP_SHW_V1_H1_1 0x0125 ++#define TX_HDCP_SHW_V1_H1_2 0x0126 ++#define TX_HDCP_SHW_V1_H1_3 0x0127 ++#define TX_HDCP_SHW_V1_H2_0 0x0128 ++#define TX_HDCP_SHW_V1_H2_1 0x0129 ++#define TX_HDCP_SHW_V1_H2_2 0x012A ++#define TX_HDCP_SHW_V1_H2_3 0x012B ++#define TX_HDCP_SHW_V1_H3_0 0x012C ++#define TX_HDCP_SHW_V1_H3_1 0x012D ++#define TX_HDCP_SHW_V1_H3_2 0x012E ++#define TX_HDCP_SHW_V1_H3_3 0x012F ++#define TX_HDCP_SHW_V1_H4_0 0x0130 ++#define TX_HDCP_SHW_V1_H4_1 0x0131 ++#define TX_HDCP_SHW_V1_H4_2 0x0132 ++#define TX_HDCP_SHW_V1_H4_3 0x0133 ++#define TX_HDCP_SHW_BCAPS 0x0140 ++#define TX_HDCP_SHW_BSTATUS_0 0x0141 ++#define TX_HDCP_SHW_BSTATUS_1 0x0142 ++#define TX_HDCP_SHW_KSV_FIFO 0x0143 ++ ++// system status 0 ++#define TX_SYSST0_CONNECT_FIFO 0x0180 ++#define TX_SYSST0_PLL_MONITOR 0x0181 ++#define TX_SYSST0_AFE_FIFO 0x0182 ++#define TX_SYSST0_ROM_STATUS 0x018F ++ ++// hdcp status ++#define TX_HDCP_ST_AUTHENTICATION 0x0190 ++#define TX_HDCP_ST_FRAME_COUNT 0x0191 ++#define TX_HDCP_ST_STATUS_0 0x0192 ++#define TX_HDCP_ST_STATUS_1 0x0193 ++#define TX_HDCP_ST_STATUS_2 0x0194 ++#define TX_HDCP_ST_STATUS_3 0x0195 ++#define TX_HDCP_ST_EDID_STATUS 0x0196 ++ #define TX_HDCP_ST_EDID_STATUS_SYSTEM_STATUS GENMASK(7, 6) ++ #define TX_HDCP_ST_EDID_STATUS_SYSTEM_STATUS_NO_SINK_ATTACHED 0x0 ++ #define TX_HDCP_ST_EDID_STATUS_SYSTEM_STATUS_READING_EDID 0x1 ++ #define TX_HDCP_ST_EDID_STATUS_SYSTEM_STATUS_DVI_MODE 0x2 ++ #define TX_HDCP_ST_EDID_STATUS_SYSTEM_STATUS_HDMI_MODE 0x3 ++ #define TX_HDCP_ST_EDID_STATUS_EDID_DATA_READY BIT(4) ++ #define TX_HDCP_ST_EDID_STATUS_HPD_STATUS BIT(1) ++ ++#define TX_HDCP_ST_MEM_STATUS 0x0197 ++#define TX_HDCP_ST_ST_MODE 0x019F ++ ++// video status ++#define TX_VIDEO_ST_ACTIVE_PIXELS_1 0x01A0 ++#define TX_VIDEO_ST_ACTIVE_PIXELS_2 0x01A1 ++#define TX_VIDEO_ST_FRONT_PIXELS 0x01A2 ++#define TX_VIDEO_ST_HSYNC_PIXELS 0x01A3 ++#define TX_VIDEO_ST_BACK_PIXELS 0x01A4 ++#define TX_VIDEO_ST_ACTIVE_LINES_1 0x01A5 ++#define TX_VIDEO_ST_ACTIVE_LINES_2 0x01A6 ++#define TX_VIDEO_ST_EOF_LINES 0x01A7 ++#define TX_VIDEO_ST_VSYNC_LINES 0x01A8 ++#define TX_VIDEO_ST_SOF_LINES 0x01A9 ++#define TX_VIDEO_ST_DTV_TIMING 0x01AA ++#define TX_VIDEO_ST_DTV_MODE 0x01AB ++// audio status ++#define TX_VIDEO_ST_AUDIO_STATUS 0x01AC ++#define TX_AFE_STATUS_0 0x01AE ++#define TX_AFE_STATUS_1 0x01AF ++ ++#define TX_IEC60958_ST_SUB1_OFFSET 0x01B0 ++#define TX_IEC60958_ST_SUB2_OFFSET 0x01C8 ++ ++// system status 1 ++#define TX_SYSST1_CALIB_BIT_RESULT_0 0x01E0 ++#define TX_SYSST1_CALIB_BIT_RESULT_1 0x01E1 ++//HDMI_STATUS_OUT[7:0] ++#define TX_HDMI_PHY_READBACK_0 0x01E2 ++//HDMI_COMP_OUT[4] ++//HDMI_STATUS_OUT[11:8] ++#define TX_HDMI_PHY_READBACK_1 0x01E3 ++#define TX_SYSST1_CALIB_BIT_RESULT_4 0x01E4 ++#define TX_SYSST1_CALIB_BIT_RESULT_5 0x01E5 ++#define TX_SYSST1_CALIB_BIT_RESULT_6 0x01E6 ++#define TX_SYSST1_CALIB_BIT_RESULT_7 0x01E7 ++#define TX_SYSST1_CALIB_BUS_RESULT_0 0x01E8 ++#define TX_SYSST1_CALIB_BUS_RESULT_1 0x01E9 ++#define TX_SYSST1_CALIB_BUS_RESULT_2 0x01EA ++#define TX_SYSST1_CALIB_BUS_RESULT_3 0x01EB ++#define TX_SYSST1_CALIB_BUS_RESULT_4 0x01EC ++#define TX_SYSST1_CALIB_BUS_RESULT_5 0x01ED ++#define TX_SYSST1_CALIB_BUS_RESULT_6 0x01EE ++#define TX_SYSST1_CALIB_BUS_RESULT_7 0x01EF ++ ++// Packet status ++#define TX_PACKET_ST_REQUEST_STATUS_1 0x01F0 ++#define TX_PACKET_ST_REQUEST_STATUS_2 0x01F1 ++#define TX_PACKET_ST_REQUEST_MISSED_1 0x01F2 ++#define TX_PACKET_ST_REQUEST_MISSED_2 0x01F3 ++#define TX_PACKET_ST_ENCODE_STATUS_0 0x01F4 ++#define TX_PACKET_ST_ENCODE_STATUS_1 0x01F5 ++#define TX_PACKET_ST_ENCODE_STATUS_2 0x01F6 ++#define TX_PACKET_ST_TIMER_STATUS 0x01F7 ++ ++// tmds status ++#define TX_TMDS_ST_CLOCK_METER_1 0x01F8 ++#define TX_TMDS_ST_CLOCK_METER_2 0x01F9 ++#define TX_TMDS_ST_CLOCK_METER_3 0x01FA ++#define TX_TMDS_ST_TMDS_STATUS_1 0x01FC ++#define TX_TMDS_ST_TMDS_STATUS_2 0x01FD ++#define TX_TMDS_ST_TMDS_STATUS_3 0x01FE ++#define TX_TMDS_ST_TMDS_STATUS_4 0x01FF ++ ++// Packet register ++#define TX_PKT_REG_SPD_INFO_BASE_ADDR 0x0200 ++#define TX_PKT_REG_VEND_INFO_BASE_ADDR 0x0220 ++#define TX_PKT_REG_MPEG_INFO_BASE_ADDR 0x0240 ++#define TX_PKT_REG_AVI_INFO_BASE_ADDR 0x0260 ++#define TX_PKT_REG_AUDIO_INFO_BASE_ADDR 0x0280 ++#define TX_PKT_REG_ACP_INFO_BASE_ADDR 0x02A0 ++#define TX_PKT_REG_ISRC1_BASE_ADDR 0x02C0 ++#define TX_PKT_REG_ISRC2_BASE_ADDR 0x02E0 ++#define TX_PKT_REG_EXCEPT0_BASE_ADDR 0x0300 ++#define TX_PKT_REG_EXCEPT1_BASE_ADDR 0x0320 ++#define TX_PKT_REG_EXCEPT2_BASE_ADDR 0x0340 ++#define TX_PKT_REG_EXCEPT3_BASE_ADDR 0x0360 ++#define TX_PKT_REG_EXCEPT4_BASE_ADDR 0x0380 ++#define TX_PKT_REG_GAMUT_P0_BASE_ADDR 0x03A0 ++#define TX_PKT_REG_GAMUT_P1_1_BASE_ADDR 0x03C0 ++#define TX_PKT_REG_GAMUT_P1_2_BASE_ADDR 0x03E0 ++ ++#define TX_RX_EDID_OFFSET 0x0600 ++ ++/* HDMI OTHER registers */ ++ ++#define HDMI_OTHER_CTRL0 0x8000 ++#define HDMI_OTHER_CTRL1 0x8001 ++ #define HDMI_OTHER_CTRL1_POWER_ON BIT(15) ++ #define HDMI_OTHER_CTRL1_HDMI_AUDIO_CLOCK_ON BIT(13) ++ ++#define HDMI_OTHER_STATUS0 0x8002 ++#define HDMI_OTHER_CTRL2 0x8003 ++#define HDMI_OTHER_INTR_MASKN 0x8004 ++ #define HDMI_OTHER_INTR_MASKN_TX_EDID_INT_RISE BIT(2) ++ #define HDMI_OTHER_INTR_MASKN_TX_HPD_INT_FALL BIT(1) ++ #define HDMI_OTHER_INTR_MASKN_TX_HPD_INT_RISE BIT(0) ++ ++#define HDMI_OTHER_INTR_STAT 0x8005 ++ #define HDMI_OTHER_INTR_STAT_EDID_RISING BIT(2) ++ #define HDMI_OTHER_INTR_STAT_HPD_FALLING BIT(1) ++ #define HDMI_OTHER_INTR_STAT_HPD_RISING BIT(0) ++ ++#define HDMI_OTHER_INTR_STAT_CLR 0x8006 ++ #define HDMI_OTHER_INTR_STAT_CLR_EDID_RISING BIT(2) ++ #define HDMI_OTHER_INTR_STAT_CLR_HPD_FALLING BIT(1) ++ #define HDMI_OTHER_INTR_STAT_CLR_HPD_RISING BIT(0) ++ ++#define HDMI_OTHER_AVI_INTR_MASKN0 0x8008 ++#define HDMI_OTHER_AVI_INTR_MASKN1 0x8009 ++#define HDMI_OTHER_RX_AINFO_INTR_MASKN0 0x800a ++#define HDMI_OTHER_RX_AINFO_INTR_MASKN1 0x800b ++#define HDMI_OTHER_RX_PACKET_INTR_CLR 0x800c ++ ++#endif /* __MESON_TRANSWITCH_HDMI_H__ */ +diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c +index 2a82119e..a2c1bf1a 100644 +--- a/drivers/gpu/drm/meson/meson_vclk.c ++++ b/drivers/gpu/drm/meson/meson_vclk.c +@@ -732,6 +732,11 @@ meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq) + return MODE_CLOCK_HIGH; + } + ++ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) ++ return MODE_OK; ++ + if (meson_hdmi_pll_find_params(priv, freq, &m, &frac, &od)) + return MODE_OK; + +@@ -784,6 +789,11 @@ meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq, + return MODE_CLOCK_HIGH; + } + ++ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) ++ return MODE_OK; ++ + for (i = 0 ; params[i].pixel_freq ; ++i) { + DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n", + i, params[i].pixel_freq, +@@ -1024,6 +1034,128 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq, + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, VCLK_EN, VCLK_EN); + } + ++static int meson_vclk_set_rate_exclusive(struct meson_drm *priv, ++ enum vpu_bulk_clk_id clk_id, ++ unsigned int rate_khz) ++{ ++ struct clk *clk = priv->vid_clks[clk_id].clk; ++ int ret; ++ ++ ret = clk_set_rate_exclusive(clk, rate_khz * 1000UL); ++ if (ret) ++ return ret; ++ ++ priv->vid_clk_rate_exclusive[clk_id] = true; ++ ++ return 0; ++} ++ ++static void meson_vclk_disable_ccf(struct meson_drm *priv) ++{ ++ unsigned int i; ++ ++ /* allow all clocks to be changed in _enable again */ ++ for (i = 0; i < VPU_VID_CLK_NUM; i++) { ++ if (!priv->vid_clk_rate_exclusive[i]) ++ continue; ++ ++ clk_rate_exclusive_put(priv->vid_clks[i].clk); ++ priv->vid_clk_rate_exclusive[i] = false; ++ } ++ ++ if (priv->clk_dac_enabled) { ++ clk_disable(priv->clk_dac); ++ priv->clk_dac_enabled = false; ++ } ++ ++ if (priv->clk_venc_enabled) { ++ clk_disable(priv->clk_venc); ++ priv->clk_venc_enabled = false; ++ } ++} ++ ++static int meson_vclk_enable_ccf(struct meson_drm *priv, unsigned int target, ++ bool hdmi_use_enci, unsigned int phy_freq, ++ unsigned int dac_freq, unsigned int venc_freq) ++{ ++ enum vpu_bulk_clk_id venc_clk_id, dac_clk_id; ++ int ret; ++ ++ if (target == MESON_VCLK_TARGET_CVBS || hdmi_use_enci) ++ venc_clk_id = VPU_VID_CLK_CTS_ENCI; ++ else ++ venc_clk_id = VPU_VID_CLK_CTS_ENCP; ++ ++ if (target == MESON_VCLK_TARGET_CVBS) ++ dac_clk_id = VPU_VID_CLK_CTS_VDAC0; ++ else ++ dac_clk_id = VPU_VID_CLK_HDMI_TX_PIXEL; ++ ++ /* ++ * The TMDS clock also updates the PLL. Protect the PLL rate so all ++ * following clocks are derived from the PLL setting which matches the ++ * TMDS clock. ++ */ ++ ret = meson_vclk_set_rate_exclusive(priv, VPU_VID_CLK_TMDS, phy_freq); ++ if (ret) { ++ dev_err(priv->dev, "Failed to set TMDS clock to %ukHz: %d\n", ++ phy_freq, ret); ++ goto out_enable_clocks; ++ } ++ ++ /* ++ * The DAC clock may be derived from a parent of the VENC clock so we ++ * must protect the VENC clock from changing it's rate. This works ++ * because the DAC freq can be divided by the VENC clock. ++ */ ++ ret = meson_vclk_set_rate_exclusive(priv, venc_clk_id, venc_freq); ++ if (ret) { ++ dev_warn(priv->dev, ++ "Failed to set VENC clock to %ukHz while TMDS clock is %ukHz: %d\n", ++ venc_freq, phy_freq, ret); ++ goto out_enable_clocks; ++ } ++ ++ priv->clk_venc = priv->vid_clks[venc_clk_id].clk; ++ ++ /* ++ * after changing any of the VID_PLL_* clocks (which can happen when ++ * update the VENC clock rate) we need to assert and then de-assert the ++ * VID_DIVIDER_CNTL_* reset lines. ++ */ ++ reset_control_bulk_assert(VPU_RESET_VID_PLL_NUM, priv->vid_pll_resets); ++ reset_control_bulk_deassert(VPU_RESET_VID_PLL_NUM, priv->vid_pll_resets); ++ ++ ret = meson_vclk_set_rate_exclusive(priv, dac_clk_id, dac_freq); ++ if (ret) { ++ dev_warn(priv->dev, ++ "Failed to set pixel clock to %ukHz while TMDS clock is %ukHz: %d\n", ++ dac_freq, phy_freq, ret); ++ goto out_enable_clocks; ++ } ++ ++ priv->clk_dac = priv->vid_clks[dac_clk_id].clk; ++ ++out_enable_clocks: ++ ret = clk_enable(priv->clk_venc); ++ if (ret) ++ dev_err(priv->dev, ++ "Failed to re-enable the VENC clock at %ukHz: %d\n", ++ venc_freq, ret); ++ else ++ priv->clk_venc_enabled = true; ++ ++ ret = clk_enable(priv->clk_dac); ++ if (ret) ++ dev_err(priv->dev, ++ "Failed to re-enable the pixel clock at %ukHz: %d\n", ++ dac_freq, ret); ++ else ++ priv->clk_dac_enabled = true; ++ ++ return ret; ++} ++ + void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + unsigned int phy_freq, unsigned int vclk_freq, + unsigned int venc_freq, unsigned int dac_freq, +@@ -1034,6 +1166,20 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + unsigned int hdmi_tx_div; + unsigned int venc_div; + ++ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) || ++ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) { ++ /* CVBS video clocks are generated off a 1296MHz base clock */ ++ if (target == MESON_VCLK_TARGET_CVBS) ++ phy_freq = 1296000; ++ ++ dev_err(priv->dev, "%s(target: %u, phy: %u, dac: %u, venc: %u, hdmi_use_enci: %u)\n", __func__, target, phy_freq, dac_freq, venc_freq, hdmi_use_enci); ++ meson_vclk_disable_ccf(priv); ++ meson_vclk_enable_ccf(priv, target, hdmi_use_enci, phy_freq, ++ dac_freq, venc_freq); ++ return; ++ } ++ + if (target == MESON_VCLK_TARGET_CVBS) { + meson_venci_cvbs_clock_config(priv); + return; +diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c +index 3bf0d6e4..d834359c 100644 +--- a/drivers/gpu/drm/meson/meson_venc.c ++++ b/drivers/gpu/drm/meson/meson_venc.c +@@ -62,10 +62,6 @@ + + /* HHI Registers */ + #define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */ +-#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ +-#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbb offset in data sheet */ +-#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ +-#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbc offset in data sheet */ + #define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 offset in data sheet */ + + struct meson_cvbs_enci_mode meson_cvbs_enci_pal = { +@@ -1957,31 +1953,47 @@ void meson_venc_enable_vsync(struct meson_drm *priv) + writel_relaxed(VENC_INTCTRL_ENCI_LNRST_INT_EN, + priv->io_base + _REG(VENC_INTCTRL)); + } +- regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), BIT(25)); ++ ++ if (priv->intr_clks[0].clk) { ++ if (!priv->intr_clks_enabled) { ++ int ret; ++ ++ ret = clk_bulk_enable(priv->num_intr_clks, ++ priv->intr_clks); ++ if (ret) ++ dev_err(priv->dev, ++ "Failed to enable the interrupt clocks\n"); ++ else ++ priv->intr_clks_enabled = true; ++ } ++ } else { ++ regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), BIT(25)); ++ } + } + + void meson_venc_disable_vsync(struct meson_drm *priv) + { +- regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), 0); ++ if (priv->intr_clks[0].clk) { ++ if (priv->intr_clks_enabled) { ++ clk_bulk_disable(priv->num_intr_clks, ++ priv->intr_clks); ++ priv->intr_clks_enabled = false; ++ } ++ } else { ++ regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), 0); ++ } ++ + writel_relaxed(0, priv->io_base + _REG(VENC_INTCTRL)); + } + + void meson_venc_init(struct meson_drm *priv) + { +- /* Disable CVBS VDAC */ +- if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { +- regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0); +- regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 8); +- } else { +- regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); +- regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8); +- } +- + /* Power Down Dacs */ + writel_relaxed(0xff, priv->io_base + _REG(VENC_VDAC_SETTING)); + + /* Disable HDMI PHY */ +- regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0); ++ if (priv->hhi) ++ regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0); + + /* Disable HDMI */ + writel_bits_relaxed(VPU_HDMI_ENCI_DATA_TO_HDMI | +diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c +index cd399b0b..bdfa342c 100644 +--- a/drivers/gpu/drm/meson/meson_viu.c ++++ b/drivers/gpu/drm/meson/meson_viu.c +@@ -448,13 +448,17 @@ void meson_viu_init(struct meson_drm *priv) + writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); + writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT)); + +- /* Set OSD alpha replace value */ +- writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, +- 0xff << OSD_REPLACE_SHIFT, +- priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); +- writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, +- 0xff << OSD_REPLACE_SHIFT, +- priv->io_base + _REG(VIU_OSD2_CTRL_STAT2)); ++ if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) && ++ !meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) && ++ !meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) { ++ /* Set OSD alpha replace value */ ++ writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, ++ 0xff << OSD_REPLACE_SHIFT, ++ priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); ++ writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, ++ 0xff << OSD_REPLACE_SHIFT, ++ priv->io_base + _REG(VIU_OSD2_CTRL_STAT2)); ++ } + + /* Disable VD1 AFBC */ + /* di_mif0_en=0 mif0_to_vpp_en=0 di_mad_en=0 and afbc vd1 set=0*/ +diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig +index ce7ba3eb..671435b6 100644 +--- a/drivers/phy/amlogic/Kconfig ++++ b/drivers/phy/amlogic/Kconfig +@@ -25,6 +25,16 @@ config PHY_MESON8B_USB2 + Meson8b and GXBB SoCs. + If unsure, say N. + ++config PHY_MESON_CVBS_DAC ++ tristate "Amlogic Meson CVBS DAC PHY driver" ++ depends on ARCH_MESON || COMPILE_TEST ++ depends on OF ++ select MFD_SYSCON ++ help ++ Enable this to support the CVBS DAC (PHY) found in Amlogic ++ Meson SoCs. ++ If unsure, say N. ++ + config PHY_MESON_GXL_USB2 + tristate "Meson GXL and GXM USB2 PHY drivers" + default ARCH_MESON +diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile +index 91e3b979..f6c38f73 100644 +--- a/drivers/phy/amlogic/Makefile ++++ b/drivers/phy/amlogic/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_PHY_MESON8_HDMI_TX) += phy-meson8-hdmi-tx.o + obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o ++obj-$(CONFIG_PHY_MESON_CVBS_DAC) += phy-meson-cvbs-dac.o + obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o + obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o + obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o +diff --git a/drivers/phy/amlogic/phy-meson-cvbs-dac.c b/drivers/phy/amlogic/phy-meson-cvbs-dac.c +new file mode 100644 +index 00000000..96549e63 +--- /dev/null ++++ b/drivers/phy/amlogic/phy-meson-cvbs-dac.c +@@ -0,0 +1,375 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (C) 2016 BayLibre, SAS ++ * Copyright (C) 2021 Martin Blumenstingl ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define HHI_VDAC_CNTL0_MESON8 0x2F4 /* 0xbd offset in data sheet */ ++#define HHI_VDAC_CNTL1_MESON8 0x2F8 /* 0xbe offset in data sheet */ ++ ++#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbd offset in data sheet */ ++#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbe offset in data sheet */ ++ ++enum phy_meson_cvbs_dac_reg { ++ MESON_CDAC_CTRL_RESV1, ++ MESON_CDAC_CTRL_RESV2, ++ MESON_CDAC_VREF_ADJ, ++ MESON_CDAC_RL_ADJ, ++ MESON_CDAC_CLK_PHASE_SEL, ++ MESON_CDAC_DRIVER_ADJ, ++ MESON_CDAC_EXT_VREF_EN, ++ MESON_CDAC_BIAS_C, ++ MESON_VDAC_CNTL0_RESERVED, ++ MESON_CDAC_GSW, ++ MESON_CDAC_PWD, ++ MESON_VDAC_CNTL1_RESERVED, ++ MESON_CVBS_DAC_NUM_REGS ++}; ++ ++struct phy_meson_cvbs_dac_data { ++ const struct reg_field *reg_fields; ++ u8 cdac_ctrl_resv2_enable_val; ++ u8 cdac_vref_adj_enable_val; ++ u8 cdac_rl_adj_enable_val; ++ bool disable_ignore_cdac_pwd; ++ bool needs_cvbs_trimming_nvmem_cell; ++}; ++ ++struct phy_meson_cvbs_dac_priv { ++ struct regmap_field *regs[MESON_CVBS_DAC_NUM_REGS]; ++ const struct phy_meson_cvbs_dac_data *data; ++ u8 cdac_gsw_enable_val; ++}; ++ ++static const struct reg_field phy_meson8_cvbs_dac_reg_fields[] = { ++ [MESON_CDAC_CTRL_RESV1] = REG_FIELD(HHI_VDAC_CNTL0_MESON8, 0, 7), ++ [MESON_CDAC_CTRL_RESV2] = REG_FIELD(HHI_VDAC_CNTL0_MESON8, 8, 15), ++ [MESON_CDAC_VREF_ADJ] = REG_FIELD(HHI_VDAC_CNTL0_MESON8, 16, 20), ++ [MESON_CDAC_RL_ADJ] = REG_FIELD(HHI_VDAC_CNTL0_MESON8, 21, 23), ++ [MESON_CDAC_CLK_PHASE_SEL] = REG_FIELD(HHI_VDAC_CNTL0_MESON8, 24, 24), ++ [MESON_CDAC_DRIVER_ADJ] = REG_FIELD(HHI_VDAC_CNTL0_MESON8, 25, 25), ++ [MESON_CDAC_EXT_VREF_EN] = REG_FIELD(HHI_VDAC_CNTL0_MESON8, 26, 26), ++ [MESON_CDAC_BIAS_C] = REG_FIELD(HHI_VDAC_CNTL0_MESON8, 27, 27), ++ [MESON_VDAC_CNTL0_RESERVED] = REG_FIELD(HHI_VDAC_CNTL0_MESON8, 28, 31), ++ [MESON_CDAC_GSW] = REG_FIELD(HHI_VDAC_CNTL1_MESON8, 0, 2), ++ [MESON_CDAC_PWD] = REG_FIELD(HHI_VDAC_CNTL1_MESON8, 3, 3), ++ [MESON_VDAC_CNTL1_RESERVED] = REG_FIELD(HHI_VDAC_CNTL1_MESON8, 4, 31), ++}; ++ ++static const struct reg_field phy_meson_g12a_cvbs_dac_reg_fields[] = { ++ [MESON_CDAC_CTRL_RESV1] = REG_FIELD(HHI_VDAC_CNTL0_G12A, 0, 7), ++ [MESON_CDAC_CTRL_RESV2] = REG_FIELD(HHI_VDAC_CNTL0_G12A, 8, 15), ++ [MESON_CDAC_VREF_ADJ] = REG_FIELD(HHI_VDAC_CNTL0_G12A, 16, 20), ++ [MESON_CDAC_RL_ADJ] = REG_FIELD(HHI_VDAC_CNTL0_G12A, 21, 23), ++ [MESON_CDAC_CLK_PHASE_SEL] = REG_FIELD(HHI_VDAC_CNTL0_G12A, 24, 24), ++ [MESON_CDAC_DRIVER_ADJ] = REG_FIELD(HHI_VDAC_CNTL0_G12A, 25, 25), ++ [MESON_CDAC_EXT_VREF_EN] = REG_FIELD(HHI_VDAC_CNTL0_G12A, 26, 26), ++ [MESON_CDAC_BIAS_C] = REG_FIELD(HHI_VDAC_CNTL0_G12A, 27, 27), ++ [MESON_VDAC_CNTL0_RESERVED] = REG_FIELD(HHI_VDAC_CNTL0_G12A, 28, 31), ++ [MESON_CDAC_GSW] = REG_FIELD(HHI_VDAC_CNTL1_G12A, 0, 2), ++ [MESON_CDAC_PWD] = REG_FIELD(HHI_VDAC_CNTL1_G12A, 3, 3), ++ [MESON_VDAC_CNTL1_RESERVED] = REG_FIELD(HHI_VDAC_CNTL1_G12A, 4, 31), ++}; ++ ++static const struct phy_meson_cvbs_dac_data phy_meson8_cvbs_dac_data = { ++ .reg_fields = phy_meson8_cvbs_dac_reg_fields, ++ .cdac_ctrl_resv2_enable_val = 0x0, ++ .cdac_vref_adj_enable_val = 0x0, ++ .cdac_rl_adj_enable_val = 0x0, ++ .disable_ignore_cdac_pwd = false, ++ .needs_cvbs_trimming_nvmem_cell = true, ++}; ++ ++static const struct phy_meson_cvbs_dac_data phy_meson_gxbb_cvbs_dac_data = { ++ .reg_fields = phy_meson8_cvbs_dac_reg_fields, ++ .cdac_ctrl_resv2_enable_val = 0x0, ++ .cdac_vref_adj_enable_val = 0x0, ++ .cdac_rl_adj_enable_val = 0x0, ++ .disable_ignore_cdac_pwd = false, ++ .needs_cvbs_trimming_nvmem_cell = false, ++}; ++ ++static const struct phy_meson_cvbs_dac_data phy_meson_gxl_cvbs_dac_data = { ++ .reg_fields = phy_meson8_cvbs_dac_reg_fields, ++ .cdac_ctrl_resv2_enable_val = 0x0, ++ .cdac_vref_adj_enable_val = 0xf, ++ .cdac_rl_adj_enable_val = 0x0, ++ .disable_ignore_cdac_pwd = false, ++ .needs_cvbs_trimming_nvmem_cell = false, ++}; ++ ++static const struct phy_meson_cvbs_dac_data phy_meson_g12a_cvbs_dac_data = { ++ .reg_fields = phy_meson_g12a_cvbs_dac_reg_fields, ++ .cdac_ctrl_resv2_enable_val = 0x60, ++ .cdac_vref_adj_enable_val = 0x10, ++ .cdac_rl_adj_enable_val = 0x4, ++ .disable_ignore_cdac_pwd = true, ++ .needs_cvbs_trimming_nvmem_cell = false, ++}; ++ ++static int phy_meson_cvbs_dac_power_on(struct phy *phy) ++{ ++ struct phy_meson_cvbs_dac_priv *priv = phy_get_drvdata(phy); ++ ++ regmap_field_write(priv->regs[MESON_CDAC_CTRL_RESV1], 0x1); ++ regmap_field_write(priv->regs[MESON_CDAC_CTRL_RESV2], ++ priv->data->cdac_ctrl_resv2_enable_val); ++ regmap_field_write(priv->regs[MESON_CDAC_VREF_ADJ], ++ priv->data->cdac_vref_adj_enable_val); ++ regmap_field_write(priv->regs[MESON_CDAC_RL_ADJ], ++ priv->data->cdac_rl_adj_enable_val); ++ regmap_field_write(priv->regs[MESON_CDAC_GSW], ++ priv->cdac_gsw_enable_val); ++ regmap_field_write(priv->regs[MESON_CDAC_PWD], 0x0); ++ ++ return 0; ++} ++ ++static int phy_meson_cvbs_dac_power_off(struct phy *phy) ++{ ++ struct phy_meson_cvbs_dac_priv *priv = phy_get_drvdata(phy); ++ ++ regmap_field_write(priv->regs[MESON_CDAC_CTRL_RESV1], 0x0); ++ regmap_field_write(priv->regs[MESON_CDAC_CTRL_RESV2], 0x0); ++ regmap_field_write(priv->regs[MESON_CDAC_VREF_ADJ], 0x0); ++ regmap_field_write(priv->regs[MESON_CDAC_RL_ADJ], 0x0); ++ regmap_field_write(priv->regs[MESON_CDAC_GSW], 0x0); ++ ++ if (priv->data->disable_ignore_cdac_pwd) ++ regmap_field_write(priv->regs[MESON_CDAC_PWD], 0x0); ++ else ++ regmap_field_write(priv->regs[MESON_CDAC_PWD], 0x1); ++ ++ return 0; ++} ++ ++static int phy_meson_cvbs_dac_init(struct phy *phy) ++{ ++ struct phy_meson_cvbs_dac_priv *priv = phy_get_drvdata(phy); ++ ++ regmap_field_write(priv->regs[MESON_CDAC_CLK_PHASE_SEL], 0x0); ++ regmap_field_write(priv->regs[MESON_CDAC_DRIVER_ADJ], 0x0); ++ regmap_field_write(priv->regs[MESON_CDAC_EXT_VREF_EN], 0x0); ++ regmap_field_write(priv->regs[MESON_CDAC_BIAS_C], 0x0); ++ regmap_field_write(priv->regs[MESON_VDAC_CNTL0_RESERVED], 0x0); ++ regmap_field_write(priv->regs[MESON_VDAC_CNTL1_RESERVED], 0x0); ++ ++ return phy_meson_cvbs_dac_power_off(phy); ++} ++ ++static const struct phy_ops phy_meson_cvbs_dac_ops = { ++ .init = phy_meson_cvbs_dac_init, ++ .power_on = phy_meson_cvbs_dac_power_on, ++ .power_off = phy_meson_cvbs_dac_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static u8 phy_meson_cvbs_trimming_version(u8 trimming1) ++{ ++ if ((trimming1 & 0xf0) == 0xa0) ++ return 5; ++ else if ((trimming1 & 0xf0) == 0x40) ++ return 2; ++ else if ((trimming1 & 0xc0) == 0x80) ++ return 1; ++ else if ((trimming1 & 0xc0) == 0x00) ++ return 0; ++ else ++ return 0xff; ++} ++ ++static int phy_meson_cvbs_read_trimming(struct device *dev, ++ struct phy_meson_cvbs_dac_priv *priv) ++{ ++ struct nvmem_cell *cell; ++ u8 *trimming; ++ size_t len; ++ ++ cell = devm_nvmem_cell_get(dev, "cvbs_trimming"); ++ if (IS_ERR(cell)) ++ return dev_err_probe(dev, PTR_ERR(cell), ++ "Failed to get the 'cvbs_trimming' nvmem-cell\n"); ++ ++ trimming = nvmem_cell_read(cell, &len); ++ if (IS_ERR(trimming)) { ++ /* ++ * TrustZone firmware may block access to the CVBS trimming ++ * data stored in the eFuse. On those devices the trimming data ++ * is stored in the u-boot environment. However, the known ++ * examples of trimming data in the u-boot environment are all ++ * zero. ++ */ ++ dev_dbg(dev, ++ "Failed to read the 'cvbs_trimming' nvmem-cell - falling back to a default value\n"); ++ priv->cdac_gsw_enable_val = 0x0; ++ return 0; ++ } ++ ++ if (len != 2) { ++ kfree(trimming); ++ return dev_err_probe(dev, -EINVAL, ++ "Read the 'cvbs_trimming' nvmem-cell with invalid length\n"); ++ } ++ ++ switch (phy_meson_cvbs_trimming_version(trimming[1])) { ++ case 1: ++ case 2: ++ case 5: ++ priv->cdac_gsw_enable_val = trimming[0] & 0x7; ++ break; ++ default: ++ priv->cdac_gsw_enable_val = 0x0; ++ break; ++ } ++ ++ kfree(trimming); ++ ++ return 0; ++} ++ ++static int phy_meson_cvbs_dac_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct phy_meson_cvbs_dac_priv *priv; ++ struct phy_provider *phy_provider; ++ struct device *dev = &pdev->dev; ++ struct regmap *hhi; ++ struct phy *phy; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ if (np) { ++ priv->data = device_get_match_data(dev); ++ if (!priv->data) ++ return dev_err_probe(dev, -EINVAL, ++ "Could not find the OF match data\n"); ++ ++ hhi = syscon_node_to_regmap(np->parent); ++ if (IS_ERR(hhi)) ++ return dev_err_probe(dev, PTR_ERR(hhi), ++ "Failed to get the parent syscon\n"); ++ } else { ++ const struct platform_device_id *pdev_id; ++ ++ pdev_id = platform_get_device_id(pdev); ++ if (!pdev_id) ++ return dev_err_probe(dev, -EINVAL, ++ "Failed to find platform device ID\n"); ++ ++ priv->data = (void *)pdev_id->driver_data; ++ if (!priv->data) ++ return dev_err_probe(dev, -EINVAL, ++ "Could not find the platform driver data\n"); ++ ++ hhi = syscon_regmap_lookup_by_compatible("amlogic,meson-gx-hhi-sysctrl"); ++ if (IS_ERR(hhi)) ++ return dev_err_probe(dev, PTR_ERR(hhi), ++ "Failed to get the \"amlogic,meson-gx-hhi-sysctrl\" syscon\n"); ++ } ++ ++ if (priv->data->needs_cvbs_trimming_nvmem_cell) { ++ ret = phy_meson_cvbs_read_trimming(dev, priv); ++ if (ret) ++ return ret; ++ } ++ ++ ret = devm_regmap_field_bulk_alloc(dev, hhi, priv->regs, ++ priv->data->reg_fields, ++ MESON_CVBS_DAC_NUM_REGS); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to create regmap fields\n"); ++ ++ phy = devm_phy_create(dev, np, &phy_meson_cvbs_dac_ops); ++ if (IS_ERR(phy)) ++ return PTR_ERR(phy); ++ ++ phy_set_drvdata(phy, priv); ++ ++ if (np) { ++ phy_provider = devm_of_phy_provider_register(dev, ++ of_phy_simple_xlate); ++ ret = PTR_ERR_OR_ZERO(phy_provider); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to register PHY provider\n"); ++ } ++ ++ platform_set_drvdata(pdev, phy); ++ ++ return 0; ++} ++ ++static const struct of_device_id phy_meson_cvbs_dac_of_match[] = { ++ { ++ .compatible = "amlogic,meson8-cvbs-dac", ++ .data = &phy_meson8_cvbs_dac_data, ++ }, ++ { ++ .compatible = "amlogic,meson-gxbb-cvbs-dac", ++ .data = &phy_meson_gxbb_cvbs_dac_data, ++ }, ++ { ++ .compatible = "amlogic,meson-gxl-cvbs-dac", ++ .data = &phy_meson_gxl_cvbs_dac_data, ++ }, ++ { ++ .compatible = "amlogic,meson-g12a-cvbs-dac", ++ .data = &phy_meson_g12a_cvbs_dac_data, ++ }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, phy_meson_cvbs_dac_of_match); ++ ++/* ++ * The platform_device_id table is used for backwards compatibility with old ++ * .dtbs which don't have a CVBS DAC node (where the VPU DRM driver registers ++ * this as a platform device. Support for additional SoCs should only be added ++ * to the of_device_id table above. ++ */ ++static const struct platform_device_id phy_meson_cvbs_dac_device_ids[] = { ++ { ++ .name = "meson-gxbb-cvbs-dac", ++ .driver_data = (kernel_ulong_t)&phy_meson_gxbb_cvbs_dac_data, ++ }, ++ { ++ .name = "meson-gxl-cvbs-dac", ++ .driver_data = (kernel_ulong_t)&phy_meson_gxl_cvbs_dac_data, ++ }, ++ { ++ .name = "meson-g12a-cvbs-dac", ++ .driver_data = (kernel_ulong_t)&phy_meson_g12a_cvbs_dac_data, ++ }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(platform, phy_meson_cvbs_dac_device_ids); ++ ++static struct platform_driver phy_meson_cvbs_dac_driver = { ++ .driver = { ++ .name = "phy-meson-cvbs-dac", ++ .of_match_table = phy_meson_cvbs_dac_of_match, ++ }, ++ .id_table = phy_meson_cvbs_dac_device_ids, ++ .probe = phy_meson_cvbs_dac_probe, ++}; ++module_platform_driver(phy_meson_cvbs_dac_driver); ++ ++MODULE_AUTHOR("Martin Blumenstingl "); ++MODULE_DESCRIPTION("Amlogic Meson CVBS DAC driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.34.1 + diff --git a/patch/kernel/archive/meson-6.7/generic-0002-m8-m8b-m8m2-drm-forcefully-enable-XRGB-format.patch b/patch/kernel/archive/meson-6.7/generic-0002-m8-m8b-m8m2-drm-forcefully-enable-XRGB-format.patch new file mode 100644 index 0000000000..7114e0dd81 --- /dev/null +++ b/patch/kernel/archive/meson-6.7/generic-0002-m8-m8b-m8m2-drm-forcefully-enable-XRGB-format.patch @@ -0,0 +1,27 @@ +From d11b44bf44111b9f1d5497e92826df46ff065dbd Mon Sep 17 00:00:00 2001 +From: hzy +Date: Sat, 18 Nov 2023 01:22:03 +0800 +Subject: [PATCH 2/3] meson8/meson8b/meson8m2: drm: Forcefully enable XRGB + format + +Signed-off-by: hzy +--- + drivers/gpu/drm/meson/meson_plane.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c +index 27e39577..027b2fe7 100644 +--- a/drivers/gpu/drm/meson/meson_plane.c ++++ b/drivers/gpu/drm/meson/meson_plane.c +@@ -483,6 +483,8 @@ static const struct drm_plane_funcs meson_plane_funcs = { + static const uint32_t supported_drm_formats_m8[] = { + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, ++ DRM_FORMAT_XRGB8888, ++ DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGB565, + }; +-- +2.34.1 + diff --git a/patch/kernel/archive/meson-6.7/generic-0003-drm-meson-Support-meson-8-8b-hdmi-tx-components.patch b/patch/kernel/archive/meson-6.7/generic-0003-drm-meson-Support-meson-8-8b-hdmi-tx-components.patch new file mode 100644 index 0000000000..fdf63bf9c9 --- /dev/null +++ b/patch/kernel/archive/meson-6.7/generic-0003-drm-meson-Support-meson-8-8b-hdmi-tx-components.patch @@ -0,0 +1,26 @@ +From 99a889c050d71d663aba1ba2a706348be72ca787 Mon Sep 17 00:00:00 2001 +From: hzy +Date: Fri, 17 Nov 2023 22:54:18 +0800 +Subject: [PATCH 3/3] drm/meson: Support meson{8,8b}-hdmi-tx components + +Signed-off-by: hzy +--- + drivers/gpu/drm/meson/meson_drv.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c +index e8134e4c..c3e8fef9 100644 +--- a/drivers/gpu/drm/meson/meson_drv.c ++++ b/drivers/gpu/drm/meson/meson_drv.c +@@ -668,6 +668,8 @@ static void meson_drv_shutdown(struct platform_device *pdev) + * private structure for HHI registers. + */ + static const struct of_device_id components_dev_match[] = { ++ { .compatible = "amlogic,meson8-hdmi-tx" }, ++ { .compatible = "amlogic,meson8b-hdmi-tx" }, + { .compatible = "amlogic,meson-gxbb-dw-hdmi" }, + { .compatible = "amlogic,meson-gxl-dw-hdmi" }, + { .compatible = "amlogic,meson-gxm-dw-hdmi" }, +-- +2.34.1 + diff --git a/patch/kernel/archive/meson-6.7/generic-Revert-mmc-core-Set-HS-clock-speed-before-sending-HS-CMD13.patch b/patch/kernel/archive/meson-6.7/generic-Revert-mmc-core-Set-HS-clock-speed-before-sending-HS-CMD13.patch new file mode 100644 index 0000000000..61aa1beb1c --- /dev/null +++ b/patch/kernel/archive/meson-6.7/generic-Revert-mmc-core-Set-HS-clock-speed-before-sending-HS-CMD13.patch @@ -0,0 +1,77 @@ +From b2a6218e25682158d6c7d6029505e60577fd4562 Mon Sep 17 00:00:00 2001 +From: hzy +Date: Sat, 18 Nov 2023 01:22:04 +0800 +Subject: [PATCH] Revert "mmc: core: Set HS clock speed before sending HS + CMD13" + +This reverts commit 4bc31edebde51fcf8ad0794763b8679a7ecb5ec0. +--- + drivers/mmc/core/mmc.c | 23 ++++------------------- + 1 file changed, 4 insertions(+), 19 deletions(-) + +diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c +index 705942ed..8652fa28 100644 +--- a/drivers/mmc/core/mmc.c ++++ b/drivers/mmc/core/mmc.c +@@ -1390,17 +1390,13 @@ static int mmc_select_hs400es(struct mmc_card *card) + goto out_err; + } + +- /* +- * Bump to HS timing and frequency. Some cards don't handle +- * SEND_STATUS reliably at the initial frequency. +- */ + mmc_set_timing(host, MMC_TIMING_MMC_HS); +- mmc_set_bus_speed(card); +- + err = mmc_switch_status(card, true); + if (err) + goto out_err; + ++ mmc_set_clock(host, card->ext_csd.hs_max_dtr); ++ + /* Switch card to DDR with strobe bit */ + val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE; + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +@@ -1458,7 +1454,7 @@ static int mmc_select_hs400es(struct mmc_card *card) + static int mmc_select_hs200(struct mmc_card *card) + { + struct mmc_host *host = card->host; +- unsigned int old_timing, old_signal_voltage, old_clock; ++ unsigned int old_timing, old_signal_voltage; + int err = -EINVAL; + u8 val; + +@@ -1489,17 +1485,8 @@ static int mmc_select_hs200(struct mmc_card *card) + false, true, MMC_CMD_RETRIES); + if (err) + goto err; +- +- /* +- * Bump to HS timing and frequency. Some cards don't handle +- * SEND_STATUS reliably at the initial frequency. +- * NB: We can't move to full (HS200) speeds until after we've +- * successfully switched over. +- */ + old_timing = host->ios.timing; +- old_clock = host->ios.clock; + mmc_set_timing(host, MMC_TIMING_MMC_HS200); +- mmc_set_clock(card->host, card->ext_csd.hs_max_dtr); + + /* + * For HS200, CRC errors are not a reliable way to know the +@@ -1512,10 +1499,8 @@ static int mmc_select_hs200(struct mmc_card *card) + * mmc_select_timing() assumes timing has not changed if + * it is a switch error. + */ +- if (err == -EBADMSG) { +- mmc_set_clock(host, old_clock); ++ if (err == -EBADMSG) + mmc_set_timing(host, old_timing); +- } + } + err: + if (err) { +-- +2.34.1 + diff --git a/patch/kernel/archive/meson-6.7/generic-Revert-pwm-meson-modify-and-simplify-calculation-in-.patch b/patch/kernel/archive/meson-6.7/generic-Revert-pwm-meson-modify-and-simplify-calculation-in-.patch new file mode 100644 index 0000000000..297fc009c1 --- /dev/null +++ b/patch/kernel/archive/meson-6.7/generic-Revert-pwm-meson-modify-and-simplify-calculation-in-.patch @@ -0,0 +1,39 @@ +From 6b1a414f7bb456817cd3cb385e9f5eb9e0d9cbe7 Mon Sep 17 00:00:00 2001 +From: hzy +Date: Sat, 18 Nov 2023 01:22:04 +0800 +Subject: [PATCH] Revert "pwm: meson: modify and simplify calculation in + meson_pwm_get_state" + +This reverts commit 6b9352f3f8a1a35faf0efc1ad1807ee303467796. +--- + drivers/pwm/pwm-meson.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c +index 5bea5324..a7c9b555 100644 +--- a/drivers/pwm/pwm-meson.c ++++ b/drivers/pwm/pwm-meson.c +@@ -322,8 +322,18 @@ static int meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + channel->lo = FIELD_GET(PWM_LOW_MASK, value); + channel->hi = FIELD_GET(PWM_HIGH_MASK, value); + +- state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->lo + channel->hi); +- state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm, channel->hi); ++ if (channel->lo == 0) { ++ state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->hi); ++ state->duty_cycle = state->period; ++ } else if (channel->lo >= channel->hi) { ++ state->period = meson_pwm_cnt_to_ns(chip, pwm, ++ channel->lo + channel->hi); ++ state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm, ++ channel->hi); ++ } else { ++ state->period = 0; ++ state->duty_cycle = 0; ++ } + + state->polarity = PWM_POLARITY_NORMAL; + +-- +2.34.1 + diff --git a/patch/kernel/archive/meson-6.7/odroidc1-dts-Enable-HDMI.patch b/patch/kernel/archive/meson-6.7/odroidc1-dts-Enable-HDMI.patch new file mode 100644 index 0000000000..4edaefe9ac --- /dev/null +++ b/patch/kernel/archive/meson-6.7/odroidc1-dts-Enable-HDMI.patch @@ -0,0 +1,107 @@ +From e9d0d7bb75ff8234d717fb640c020ffe303a8d11 Mon Sep 17 00:00:00 2001 +From: Martin Blumenstingl +Date: Fri, 20 Mar 2020 15:17:51 +0100 +Subject: [PATCH] ARM: dts: meson8b: odroid-c1: enable HDMI for the + Odroid-C1 - WiP + +WiP + +Signed-off-by: Martin Blumenstingl +--- + .../arm/boot/dts/amlogic/meson8b-odroidc1.dts | 59 +++++++++++++++++++ + 1 file changed, 59 insertions(+) + +diff --git a/arch/arm/boot/dts/amlogic/meson8b-odroidc1.dts b/arch/arm/boot/dts/amlogic/meson8b-odroidc1.dts +index 94168284..7e43212e 100644 +--- a/arch/arm/boot/dts/amlogic/meson8b-odroidc1.dts ++++ b/arch/arm/boot/dts/amlogic/meson8b-odroidc1.dts +@@ -32,6 +32,17 @@ emmc_pwrseq: emmc-pwrseq { + reset-gpios = <&gpio BOOT_9 GPIO_ACTIVE_LOW>; + }; + ++ hdmi-connector { ++ compatible = "hdmi-connector"; ++ type = "a"; ++ ++ port { ++ hdmi_connector_in: endpoint { ++ remote-endpoint = <&hdmi_tx_tmds_out>; ++ }; ++ }; ++ }; ++ + leds { + compatible = "gpio-leds"; + led-blue { +@@ -93,6 +104,38 @@ rtc32k_xtal: rtc32k-xtal-clk { + #clock-cells = <0>; + }; + ++ sound { ++ compatible = "amlogic,gx-sound-card"; ++ model = "ODROID-C1"; ++ ++ assigned-clocks = <&clkc CLKID_MPLL0>, ++ <&clkc CLKID_MPLL1>; ++ assigned-clock-rates = <294912000>, ++ <270950400>; ++ ++ dai-link-0 { ++ sound-dai = <&aiu AIU_CPU CPU_I2S_FIFO>; ++ }; ++ ++ dai-link-1 { ++ sound-dai = <&aiu AIU_CPU CPU_I2S_ENCODER>; ++ dai-format = "i2s"; ++ mclk-fs = <256>; ++ ++ codec-0 { ++ sound-dai = <&aiu AIU_HDMI CTRL_I2S>; ++ }; ++ }; ++ ++ dai-link-2 { ++ sound-dai = <&aiu AIU_HDMI CTRL_OUT>; ++ ++ codec-0 { ++ sound-dai = <&hdmi_tx 0>; ++ }; ++ }; ++ }; ++ + vcc_1v8: regulator-vcc-1v8 { + /* + * RICHTEK RT9179 configured for a fixed output voltage of +@@ -187,6 +230,10 @@ vdd_rtc: regulator-vdd-rtc { + }; + }; + ++&aiu { ++ status = "okay"; ++}; ++ + &cpu0 { + cpu-supply = <&vcck>; + }; +@@ -283,6 +330,18 @@ &gpio_ao { + "SYS_LED", "", ""; + }; + ++&hdmi_tx { ++ status = "okay"; ++ pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; ++ pinctrl-names = "default"; ++}; ++ ++&hdmi_tx_tmds_port { ++ hdmi_tx_tmds_out: endpoint { ++ remote-endpoint = <&hdmi_connector_in>; ++ }; ++}; ++ + &ir_receiver { + status = "okay"; + pinctrl-0 = <&ir_recv_pins>; +-- +2.34.1 + diff --git a/patch/kernel/archive/meson-6.7/onecloud-0001-add-dts.patch b/patch/kernel/archive/meson-6.7/onecloud-0001-add-dts.patch new file mode 100644 index 0000000000..2aa1568d6e --- /dev/null +++ b/patch/kernel/archive/meson-6.7/onecloud-0001-add-dts.patch @@ -0,0 +1,441 @@ +From 5271ca000048ca86ac8f2e81ebcb1e2a0993cd73 Mon Sep 17 00:00:00 2001 +From: hzy +Date: Sat, 1 Apr 2023 13:24:42 +0800 +Subject: [PATCH 1/2] ARM: dts: meson8b: Add DTS for Xunlei Onecloud + +Signed-off-by: hzy +--- + arch/arm/boot/dts/amlogic/Makefile | 1 + + .../arm/boot/dts/amlogic/meson8b-onecloud.dts | 410 ++++++++++++++++++ + 2 files changed, 411 insertions(+) + create mode 100644 arch/arm/boot/dts/amlogic/meson8b-onecloud.dts + +diff --git a/arch/arm/boot/dts/amlogic/Makefile b/arch/arm/boot/dts/amlogic/Makefile +index a8431078..4ef5d491 100644 +--- a/arch/arm/boot/dts/amlogic/Makefile ++++ b/arch/arm/boot/dts/amlogic/Makefile +@@ -6,4 +6,5 @@ dtb-$(CONFIG_MACH_MESON8) += \ + meson8b-ec100.dtb \ + meson8b-mxq.dtb \ + meson8b-odroidc1.dtb \ ++ meson8b-onecloud.dtb \ + meson8m2-mxiii-plus.dtb +diff --git a/arch/arm/boot/dts/amlogic/meson8b-onecloud.dts b/arch/arm/boot/dts/amlogic/meson8b-onecloud.dts +new file mode 100644 +index 00000000..1fa5420f +--- /dev/null ++++ b/arch/arm/boot/dts/amlogic/meson8b-onecloud.dts +@@ -0,0 +1,410 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Author: hzy ++ */ ++ ++/dts-v1/; ++ ++#include "meson8b.dtsi" ++#include ++#include ++ ++/ { ++ model = "Xunlei OneCloud"; ++ compatible = "xunlei,onecloud", "amlogic,meson8b"; ++ ++ aliases { ++ serial0 = &uart_AO; ++ mmc0 = &sd_card_slot; ++ mmc1 = &sdhc; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x40000000 0x40000000>; ++ }; ++ ++ emmc_pwrseq: emmc-pwrseq { ++ compatible = "mmc-pwrseq-emmc"; ++ reset-gpios = <&gpio BOOT_9 GPIO_ACTIVE_LOW>; ++ }; ++ ++ button { ++ // compatible = "gpio-keys-polled"; ++ // poll-interval = <100>; ++ ++ compatible = "gpio-keys"; ++ ++ autorepeat; ++ ++ reset-button { ++ label = "reset"; ++ linux,code = ; ++ ++ // gpios = <&gpio_ao GPIOAO_5 GPIO_ACTIVE_LOW>; ++ ++ interrupt-parent = <&gpio_intc>; ++ interrupts = <5 IRQ_TYPE_LEVEL_LOW>; // GPIOAO 5 ++ }; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ red { ++ label = "onecloud:red:alive"; ++ gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; ++ ++ default-state = "on"; ++ linux,default-trigger = "default-on"; ++ }; ++ ++ green { ++ label = "onecloud:green:alive"; ++ gpios = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; ++ ++ default-state = "off"; ++ linux,default-trigger = "mmc1"; ++ }; ++ ++ blue { ++ label = "onecloud:blue:alive"; ++ gpios = <&gpio_ao GPIOAO_4 GPIO_ACTIVE_HIGH>; ++ ++ default-state = "off"; ++ linux,default-trigger = "usb-host"; ++ }; ++ }; ++ ++ p12v: regulator-p12v { ++ compatible = "regulator-fixed"; ++ ++ regulator-name = "P12V"; ++ regulator-min-microvolt = <12000000>; ++ regulator-max-microvolt = <12000000>; ++ }; ++ ++ vcc_5v: regulator-vcc-5v { ++ compatible = "regulator-fixed"; ++ ++ regulator-name = "VCC5V"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ ++ vin-supply = <&p12v>; ++ ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vcc_3v3: regulator-vcc-3v3 { ++ compatible = "regulator-fixed"; ++ ++ regulator-name = "VCC3V3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ ++ vin-supply = <&p12v>; ++ ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vcc_1v8: regulator-vcc-1v8 { ++ compatible = "regulator-fixed"; ++ ++ regulator-name = "VCC1V8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ vin-supply = <&p12v>; ++ ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vcc_ddr: regulator-vcc-ddr { ++ compatible = "regulator-fixed"; ++ ++ regulator-name = "VCC_DDR"; ++ regulator-min-microvolt = <1500000>; ++ regulator-max-microvolt = <1500000>; ++ ++ vin-supply = <&vcc_3v3>; ++ ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vcc_core: regulator-vcc-core { ++ compatible = "pwm-regulator"; ++ ++ regulator-name = "VCC_CORE"; ++ ++ // +---------------------------------------------------+ ++ // | The actual mapping in phyical | ++ // +------+--------+--------+--------+--------+--------+ ++ // | | 100% | 60% | 30% | 10% | 0% | ++ // +------+--------+--------+--------+--------+--------+ ++ // | V1.0 | 677mV | 857mV | 992mV | 1082mV | 1127mV | ++ // | V1.3 | 1116mV | 1121mV | 1125mV | 1128mV | 1129mV | ++ // +------+--------+--------+--------+--------+--------+ ++ // ++ // According to meson8b.dtsi, the CPU should be able to ++ // run at 504MHz with 870mV. But this regulator supplies ++ // not only CPU but also GPU. And according to the users' ++ // tests on V1.0, we need such higher voltages. ++ ++ pwms = <&pwm_cd 1 12001 0>; // PWM_D ++ pwm-dutycycle-range = <10 0>; ++ regulator-min-microvolt = <860000>; ++ regulator-max-microvolt = <1140000>; ++ ++ pwm-supply = <&p12v>; ++ ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++}; ++ ++&uart_AO { ++ status = "okay"; ++ pinctrl-0 = <&uart_ao_a_pins>; ++ pinctrl-names = "default"; ++}; ++ ++&pwm_cd { ++ status = "okay"; ++ pinctrl-0 = <&pwm_c1_pins>, <&pwm_d_pins>; ++ pinctrl-names = "default"; ++ clocks = <&xtal>, <&xtal>; ++ clock-names = "clkin0", "clkin1"; ++}; ++ ++&cpu0 { ++ cpu-supply = <&vcc_core>; ++}; ++ ++&saradc { ++ status = "okay"; ++ vref-supply = <&vcc_1v8>; ++}; ++ ++&mali { ++ // commented to allow cpufreq tweaking ++ // mali-supply = <&vcc_core>; ++}; ++ ++&gpio { ++ gpio-line-names = ++ /* 0 */ "WIFI_SDIO_D0 PIN18 (GPIOX_0)", ++ /* 1 */ "WIFI_SDIO_D1 PIN19 (GPIOX_1)", ++ /* 2 */ "WIFI_SDIO_D2 PIN14 (GPIOX_2)", ++ /* 3 */ "WIFI_SDIO_D3 PIN15 (GPIOX_3)", ++ /* 4 */ "WIFI_PCM_DIN PIN27 (GPIOX_4)", ++ /* 5 */ "WIFI_PCM_DOUT PIN25 (GPIOX_5)", ++ /* 6 */ "WIFI_PCM_SYNC PIN28 (GPIOX_6)", ++ /* 7 */ "WIFI_PCM_CLK PIN26 (GPIOX_7)", ++ /* 8 */ "WIFI_SDIO_CLK PIN17_Resistor (GPIOX_8)", ++ /* 9 */ "WIFI_SDIO_CMD PIN16 (GPIOX_9)", ++ /* 10 */ "GPIOX_10", ++ /* 11 */ "WIFI PIN12 (GPIOX_11)", ++ /* 12 */ "WIFI_UART_RX PIN43 (GPIOX_16)", ++ /* 13 */ "WIFI_UART_TX PIN42 (GPIOX_17)", ++ /* 14 */ "WIFI_UART_RTS PIN41_Resistor (GPIOX_18)", ++ /* 15 */ "WIFI_UART_CTS PIN44 (GPIOX_19)", ++ /* 16 */ "WIFI PIN34 (GPIOX_20)", ++ /* 17 */ "WIFI_WAKE PIN13 (GPIOX_21)", ++ ++ /* 18 */ "Resistor_TopOf_LED (GPIOY_0)", ++ /* 19 */ "GPIOY_1", ++ /* 20 */ "GPIOY_3", ++ /* 21 */ "GPIOY_6", ++ /* 22 */ "GPIOY_7", ++ /* 23 */ "GPIOY_8", ++ /* 24 */ "GPIOY_9", ++ /* 25 */ "GPIOY_10", ++ /* 26 */ "GPIOY_11", ++ /* 27 */ "GPIOY_12", ++ /* 28 */ "Left_BottomOf_CPU (GPIOY_13)", ++ /* 29 */ "Right_BottomOf_CPU (GPIOY_14)", ++ ++ /* 30 */ "GPIODV_9 (PWM_C)", ++ /* 31 */ "GPIODV_24", ++ /* 32 */ "GPIODV_25", ++ /* 33 */ "GPIODV_26", ++ /* 34 */ "GPIODV_27", ++ /* 35 */ "VCC_CPU_PWM (GPIODV_28)", ++ /* 36 */ "GPIODV_29", ++ ++ /* 37 */ "HDMI_HPD (GPIOH_0)", ++ /* 38 */ "HDMI_SDA (GPIOH_1)", ++ /* 39 */ "HDMI_SCL (GPIOH_2)", ++ /* 40 */ "ETH_PHY_INTR (GPIOH_3)", ++ /* 41 */ "ETH_PHY_nRST (GPIOH_4)", ++ /* 42 */ "ETH_TXD1 (GPIOH_5)", ++ /* 43 */ "ETH_TXD0 (GPIOH_6)", ++ /* 44 */ "ETH_TXD3 (GPIOH_7)", ++ /* 45 */ "ETH_TXD2 (GPIOH_8)", ++ /* 46 */ "ETH_TX_CLK (GPIOH_9)", ++ ++ /* 47 */ "SDCARD_D1 (CARD_0)", ++ /* 48 */ "SDCARD_D0 (CARD_1)", ++ /* 49 */ "SDCARD_CLK (CARD_2)", ++ /* 50 */ "SDCARD_CMD (CARD_3)", ++ /* 51 */ "SDCARD_D3 (CARD_4)", ++ /* 52 */ "SDCARD_D2 (CARD_5)", ++ /* 53 */ "SDCARD_CD (CARD_6)", ++ ++ /* 54 */ "EMMC_D0 (BOOT_0)", ++ /* 55 */ "EMMC_D1 (BOOT_1)", ++ /* 56 */ "EMMC_D2 (BOOT_2)", ++ /* 57 */ "EMMC_D3 (BOOT_3)", ++ /* 58 */ "EMMC_D4 (BOOT_4)", ++ /* 59 */ "EMMC_D5 (BOOT_5)", ++ /* 60 */ "EMMC_D6 (BOOT_6)", ++ /* 61 */ "EMMC_D7 (BOOT_7)", ++ /* 62 */ "EMMC_CLK (BOOT_8)", ++ /* 63 */ "EMMC_nRST (BOOT_9)", ++ /* 64 */ "EMMC_CMD (BOOT_10)", ++ /* 65 */ "BOOT_11", ++ /* 66 */ "BOOT_12", ++ /* 67 */ "BOOT_13", ++ /* 68 */ "BOOT_14", ++ /* 69 */ "BOOT_15", ++ /* 70 */ "BOOT_16", ++ /* 71 */ "BOOT_17", ++ /* 72 */ "BOOT_18", ++ ++ /* 73 */ "ETH_RXD1 (DIF_0_P)", ++ /* 74 */ "ETH_RXD0 (DIF_0_N)", ++ /* 75 */ "ETH_RX_DV (DIF_1_P)", ++ /* 76 */ "ETH_RX_CLK (DIF_1_N)", ++ /* 77 */ "ETH_RXD3 (DIF_2_P)", ++ /* 78 */ "ETH_RXD2 (DIF_2_N)", ++ /* 79 */ "ETH_TX_EN (DIF_3_P)", ++ /* 80 */ "ETH_REF_CLK (DIF_3_N)", ++ /* 81 */ "ETH_MDC (DIF_4_P)", ++ /* 82 */ "ETH_MDIO_EN (DIF_4_N)"; ++}; ++ ++&gpio_ao { ++ gpio-line-names = ++ /* 0 */ "UART TX (GPIOAO_0)", ++ /* 1 */ "UART RX (GPIOAO_1)", ++ /* 2 */ "RED_LED (GPIOAO_2)", ++ /* 3 */ "GREEN_LED (GPIOAO_3)", ++ /* 4 */ "BLUE_LED (GPIOAO_4)", ++ /* 5 */ "BUTTON (GPIOAO_5)", ++ /* 6 */ "GPIOAO_6", ++ /* 7 */ "IR_IN (GPIOAO_7)", ++ /* 8 */ "GPIOAO_8", ++ /* 9 */ "GPIOAO_9", ++ /* 10 */ "GPIOAO_10", ++ /* 11 */ "GPIOAO_11", ++ /* 12 */ "GPIOAO_12", ++ /* 13 */ "GPIOAO_13", ++ ++ /* 14 */ "GPIO_BSD_EN", ++ /* 15 */ "GPIO_TEST_N"; ++}; ++ ++// eMMC ++&sdhc { ++ status = "okay"; ++ ++ pinctrl-0 = <&sdxc_c_pins>; ++ pinctrl-names = "default"; ++ ++ non-removable; ++ bus-width = <8>; ++ max-frequency = <200000000>; ++ cap-mmc-highspeed; ++ mmc-hs200-1_8v; ++ ++ mmc-pwrseq = <&emmc_pwrseq>; ++ vmmc-supply = <&vcc_3v3>; ++ // vqmmc-supply = <&vcc_3v3>; ++}; ++ ++&sdio { ++ status = "okay"; ++ ++ pinctrl-0 = <&sd_b_pins>; ++ pinctrl-names = "default"; ++ ++ // SD card ++ sd_card_slot: slot@1 { ++ compatible = "mmc-slot"; ++ reg = <1>; ++ status = "okay"; ++ ++ bus-width = <4>; ++ no-sdio; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ disable-wp; ++ ++ cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>; ++ ++ vmmc-supply = <&vcc_3v3>; ++ // vqmmc-supply = <&vcc_3v3>; ++ }; ++}; ++ ++ðmac { ++ status = "okay"; ++ ++ pinctrl-0 = <ð_rgmii_pins>; ++ pinctrl-names = "default"; ++ ++ phy-handle = <ð_phy>; ++ phy-mode = "rgmii-rxid"; ++ ++ mdio { ++ compatible = "snps,dwmac-mdio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ // Realtek RTL8211F (0x001cc916) ++ eth_phy: ethernet-phy@0 { ++ reg = <0>; ++ ++ reset-assert-us = <10000>; ++ reset-deassert-us = <80000>; ++ reset-gpios = <&gpio GPIOH_4 GPIO_ACTIVE_LOW>; ++ ++ interrupt-parent = <&gpio_intc>; ++ interrupts = <17 IRQ_TYPE_LEVEL_LOW>; // GPIOH 3 ++ }; ++ }; ++}; ++ ++&usb0 { ++ status = "okay"; ++ dr_mode = "otg"; ++ usb-role-switch; ++ role-switch-default-mode = "host"; ++}; ++ ++&usb0_phy { ++ status = "okay"; ++}; ++ ++&usb1 { ++ status = "okay"; ++}; ++ ++&usb1_phy { ++ status = "okay"; ++}; ++ ++&ir_receiver { ++ status = "okay"; ++ pinctrl-0 = <&ir_recv_pins>; ++ pinctrl-names = "default"; ++}; +-- +2.34.1 + diff --git a/patch/kernel/archive/meson-6.7/onecloud-0002-dts-Support-HDMI.patch b/patch/kernel/archive/meson-6.7/onecloud-0002-dts-Support-HDMI.patch new file mode 100644 index 0000000000..236bcdb4f2 --- /dev/null +++ b/patch/kernel/archive/meson-6.7/onecloud-0002-dts-Support-HDMI.patch @@ -0,0 +1,96 @@ +From 40100240a3702be8f8ceb88ec6643af669bd6de8 Mon Sep 17 00:00:00 2001 +From: hzy +Date: Sat, 1 Apr 2023 10:26:14 +0800 +Subject: [PATCH 2/2] ARM: dts: meson8b: onecloud: Support HDMI + +Signed-off-by: hzy +--- + .../arm/boot/dts/amlogic/meson8b-onecloud.dts | 58 +++++++++++++++++++ + 1 file changed, 58 insertions(+) + +diff --git a/arch/arm/boot/dts/amlogic/meson8b-onecloud.dts b/arch/arm/boot/dts/amlogic/meson8b-onecloud.dts +index 1fa5420f..6ed19522 100644 +--- a/arch/arm/boot/dts/amlogic/meson8b-onecloud.dts ++++ b/arch/arm/boot/dts/amlogic/meson8b-onecloud.dts +@@ -80,6 +80,48 @@ blue { + }; + }; + ++ hdmi-connector { ++ compatible = "hdmi-connector"; ++ type = "a"; ++ ++ port { ++ hdmi_connector_in: endpoint { ++ remote-endpoint = <&hdmi_tx_tmds_out>; ++ }; ++ }; ++ }; ++ ++ sound { ++ compatible = "amlogic,gx-sound-card"; ++ ++ assigned-clocks = <&clkc CLKID_MPLL0>, ++ <&clkc CLKID_MPLL1>; ++ assigned-clock-rates = <294912000>, ++ <270950400>; ++ ++ dai-link-0 { ++ sound-dai = <&aiu AIU_CPU CPU_I2S_FIFO>; ++ }; ++ ++ dai-link-1 { ++ sound-dai = <&aiu AIU_CPU CPU_I2S_ENCODER>; ++ dai-format = "i2s"; ++ mclk-fs = <256>; ++ ++ codec-0 { ++ sound-dai = <&aiu AIU_HDMI CTRL_I2S>; ++ }; ++ }; ++ ++ dai-link-2 { ++ sound-dai = <&aiu AIU_HDMI CTRL_OUT>; ++ ++ codec-0 { ++ sound-dai = <&hdmi_tx 0>; ++ }; ++ }; ++ }; ++ + p12v: regulator-p12v { + compatible = "regulator-fixed"; + +@@ -199,6 +241,10 @@ &mali { + // mali-supply = <&vcc_core>; + }; + ++&aiu { ++ status = "okay"; ++}; ++ + &gpio { + gpio-line-names = + /* 0 */ "WIFI_SDIO_D0 PIN18 (GPIOX_0)", +@@ -403,6 +449,18 @@ &usb1_phy { + status = "okay"; + }; + ++&hdmi_tx { ++ status = "okay"; ++ pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; ++ pinctrl-names = "default"; ++}; ++ ++&hdmi_tx_tmds_port { ++ hdmi_tx_tmds_out: endpoint { ++ remote-endpoint = <&hdmi_connector_in>; ++ }; ++}; ++ + &ir_receiver { + status = "okay"; + pinctrl-0 = <&ir_recv_pins>; +-- +2.34.1 +