Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 3/3] arm64: dts: imx93-var-dart: Add support for Variscite Sonata board
From: Stefano Radaelli @ 2026-03-27  9:08 UTC (permalink / raw)
  To: linux-kernel, devicetree, imx, linux-arm-kernel
  Cc: pierluigi.p, Stefano Radaelli, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo, Dario Binacchi, Alexander Stein,
	Maud Spierings, Josua Mayer, Markus Niebel, Primoz Fiser,
	Francesco Dolcini
In-Reply-To: <cover.1774601806.git.stefano.r@variscite.com>

From: Stefano Radaelli <stefano.r@variscite.com>

Add device tree support for the Variscite Sonata carrier board with the
DART-MX93 system on module.

The Sonata board includes
- uSD Card support
- USB ports and OTG
- Additional Gigabit Ethernet interface
- Uart, SPI and I2C interfaces
- GPIO Expanders
- RTC module
- TPM module
- CAN peripherals

Link: https://variscite.com/carrier-boards/sonata-board/
Signed-off-by: Stefano Radaelli <stefano.r@variscite.com>
---
v1->v2:
 - 

 arch/arm64/boot/dts/freescale/Makefile        |   1 +
 .../dts/freescale/imx93-var-dart-sonata.dts   | 654 ++++++++++++++++++
 2 files changed, 655 insertions(+)
 create mode 100644 arch/arm64/boot/dts/freescale/imx93-var-dart-sonata.dts

diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile
index 2da6dc4f8a14..266eddd745c9 100644
--- a/arch/arm64/boot/dts/freescale/Makefile
+++ b/arch/arm64/boot/dts/freescale/Makefile
@@ -487,6 +487,7 @@ imx93-tqma9352-mba91xxca-rgb-cdtech-dc44-dtbs := imx93-tqma9352-mba91xxca.dtb im
 dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba91xxca-lvds-tm070jvhg33.dtb
 dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba91xxca-rgb-cdtech-dc44.dtb
 
+dtb-$(CONFIG_ARCH_MXC) += imx93-var-dart-sonata.dtb
 dtb-$(CONFIG_ARCH_MXC) += imx93-var-som-symphony.dtb
 dtb-$(CONFIG_ARCH_MXC) += imx93w-evk.dtb
 dtb-$(CONFIG_ARCH_MXC) += imx943-evk.dtb
diff --git a/arch/arm64/boot/dts/freescale/imx93-var-dart-sonata.dts b/arch/arm64/boot/dts/freescale/imx93-var-dart-sonata.dts
new file mode 100644
index 000000000000..5513d3b148a2
--- /dev/null
+++ b/arch/arm64/boot/dts/freescale/imx93-var-dart-sonata.dts
@@ -0,0 +1,654 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Variscite Sonata carrier board for DART-MX93
+ *
+ * Link: https://variscite.com/carrier-boards/sonata-board/
+ *
+ * Copyright (C) 2026 Variscite Ltd. - https://www.variscite.com/
+ *
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/leds/common.h>
+#include "imx93-var-dart.dtsi"
+
+/ {
+	model = "Variscite DART-MX93 on Sonata-Board";
+	compatible = "variscite,var-dart-mx93-sonata",
+		     "variscite,var-dart-mx93",
+		     "fsl,imx93";
+
+	aliases {
+		ethernet0 = &eqos;
+		ethernet1 = &fec;
+		gpio0 = &gpio1;
+		gpio1 = &gpio2;
+		gpio2 = &gpio3;
+		i2c0 = &lpi2c1;
+		i2c1 = &lpi2c2;
+		i2c2 = &lpi2c3;
+		i2c3 = &lpi2c4;
+		i2c4 = &lpi2c5;
+		mmc0 = &usdhc1;
+		mmc1 = &usdhc2;
+		serial0 = &lpuart1;
+		serial1 = &lpuart2;
+		serial2 = &lpuart3;
+		serial3 = &lpuart4;
+		serial4 = &lpuart5;
+		serial5 = &lpuart6;
+		serial6 = &lpuart7;
+	};
+
+	chosen {
+		stdout-path = &lpuart1;
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+
+		button-home {
+			label = "Home";
+			linux,code = <KEY_HOME>;
+			gpios = <&pca6408_1 4 GPIO_ACTIVE_LOW>;
+			wakeup-source;
+		};
+
+		button-up {
+			label = "Up";
+			linux,code = <KEY_UP>;
+			gpios = <&pca6408_1 5 GPIO_ACTIVE_LOW>;
+			wakeup-source;
+		};
+
+		button-down {
+			label = "Down";
+			linux,code = <KEY_DOWN>;
+			gpios = <&pca6408_1 6 GPIO_ACTIVE_LOW>;
+			wakeup-source;
+		};
+
+		button-back {
+			label = "Back";
+			linux,code = <KEY_BACK>;
+			gpios = <&pca6408_1 7 GPIO_ACTIVE_LOW>;
+			wakeup-source;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_leds_gpio>;
+
+		led-emmc {
+			gpios = <&gpio2 11 GPIO_ACTIVE_HIGH>;
+			label = "eMMC";
+			linux,default-trigger = "mmc0";
+		};
+	};
+
+	clk40m: oscillator {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <40000000>;
+		clock-output-names = "can_osc";
+	};
+
+	reg_vref_1v8: regulator-adc-vref {
+		compatible = "regulator-fixed";
+		regulator-name = "vref_1v8";
+		regulator-max-microvolt = <1800000>;
+		regulator-min-microvolt = <1800000>;
+	};
+
+	reg_usdhc2_vmmc: regulator-vmmc-usdhc2 {
+		compatible = "regulator-fixed";
+		regulator-name = "VDD_SD2_3V3";
+		off-on-delay-us = <20000>;
+		pinctrl-0 = <&pinctrl_reg_usdhc2_vmmc>;
+		pinctrl-names = "default";
+		regulator-max-microvolt = <3300000>;
+		regulator-min-microvolt = <3300000>;
+		gpio = <&gpio2 18 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+	};
+
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		ethosu_mem: ethosu-region@88000000 {
+			compatible = "shared-dma-pool";
+			reusable;
+			reg = <0x0 0x88000000 0x0 0x8000000>;
+		};
+
+		vdev0vring0: vdev0vring0@87ee0000 {
+			reg = <0 0x87ee0000 0 0x8000>;
+			no-map;
+		};
+
+		vdev0vring1: vdev0vring1@87ee8000 {
+			reg = <0 0x87ee8000 0 0x8000>;
+			no-map;
+		};
+
+		vdev1vring0: vdev1vring0@87ef0000 {
+			reg = <0 0x87ef0000 0 0x8000>;
+			no-map;
+		};
+
+		vdev1vring1: vdev1vring1@87ef8000 {
+			reg = <0 0x87ef8000 0 0x8000>;
+			no-map;
+		};
+
+		rsc_table: rsc-table@2021e000 {
+			reg = <0 0x2021e000 0 0x1000>;
+			no-map;
+		};
+
+		vdevbuffer: vdevbuffer@87f00000 {
+			compatible = "shared-dma-pool";
+			reg = <0 0x87f00000 0 0x100000>;
+			no-map;
+		};
+
+		ele_reserved: ele-reserved@87de0000 {
+			compatible = "shared-dma-pool";
+			reg = <0 0x87de0000 0 0x100000>;
+			no-map;
+		};
+	};
+};
+
+&adc1 {
+	vref-supply = <&reg_vref_1v8>;
+	status = "okay";
+};
+
+/* Use external instead of internal RTC */
+&bbnsm_rtc {
+	status = "disabled";
+};
+
+&eqos {
+	mdio {
+		ethphy1: ethernet-phy@1 {
+			compatible = "ethernet-phy-ieee802.3-c22";
+			reg = <1>;
+			reset-assert-us = <15000>;
+			reset-deassert-us = <100000>;
+			reset-gpios = <&pca6408_2 0 GPIO_ACTIVE_LOW>;
+
+			leds {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				led@0 {
+					reg = <0>;
+					color = <LED_COLOR_ID_YELLOW>;
+					function = LED_FUNCTION_LAN;
+					linux,default-trigger = "netdev";
+				};
+
+				led@1 {
+					reg = <1>;
+					color = <LED_COLOR_ID_GREEN>;
+					function = LED_FUNCTION_LAN;
+					linux,default-trigger = "netdev";
+				};
+			};
+		};
+	};
+};
+
+&ethphy0 {
+	leds {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		led@0 {
+			reg = <0>;
+			color = <LED_COLOR_ID_YELLOW>;
+			function = LED_FUNCTION_LAN;
+			linux,default-trigger = "netdev";
+		};
+
+		led@1 {
+			reg = <1>;
+			color = <LED_COLOR_ID_GREEN>;
+			function = LED_FUNCTION_LAN;
+			linux,default-trigger = "netdev";
+		};
+	};
+};
+
+&fec {
+	pinctrl-names = "default", "sleep";
+	pinctrl-0 = <&pinctrl_fec>;
+	pinctrl-1 = <&pinctrl_fec_sleep>;
+	/*
+	 * The required RGMII TX and RX 2ns delays are implemented directly
+	 * in hardware via passive delay elements on the SOM PCB.
+	 * No delay configuration is needed in software via PHY driver.
+	 */
+	phy-mode = "rgmii";
+	phy-handle = <&ethphy1>;
+	status = "okay";
+};
+
+&flexcan1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_flexcan1>;
+	status = "okay";
+};
+
+&lpi2c1 {
+	clock-frequency = <400000>;
+	pinctrl-0 = <&pinctrl_lpi2c1>;
+	pinctrl-1 = <&pinctrl_lpi2c1_gpio>;
+	pinctrl-2 = <&pinctrl_lpi2c1_gpio>;
+	pinctrl-names = "default", "sleep", "gpio";
+	scl-gpios = <&gpio1 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+	sda-gpios = <&gpio1 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+	status = "okay";
+
+	pca9534: gpio@22 {
+		compatible = "nxp,pca9534";
+		reg = <0x22>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <10 IRQ_TYPE_LEVEL_LOW>;
+	};
+
+	/* Capacitive touch controller */
+	ft5x06_ts: touchscreen@38 {
+		compatible = "edt,edt-ft5206";
+		reg = <0x38>;
+		interrupt-parent = <&gpio3>;
+		interrupts = <27 IRQ_TYPE_EDGE_FALLING>;
+		pinctrl-0 = <&pinctrl_captouch>;
+		pinctrl-names = "default";
+		reset-gpios = <&pca6408_2 4 GPIO_ACTIVE_LOW>;
+		touchscreen-inverted-x;
+		touchscreen-inverted-y;
+		touchscreen-size-x = <800>;
+		touchscreen-size-y = <480>;
+		wakeup-source;
+	};
+
+	/* USB Type-C Controller */
+	typec@3d {
+		compatible = "nxp,ptn5150";
+		reg = <0x3d>;
+		interrupt-parent = <&gpio4>;
+		interrupts = <29 IRQ_TYPE_LEVEL_HIGH>;
+		pinctrl-0 = <&pinctrl_extcon>;
+		pinctrl-names = "default";
+
+		port {
+			typec1_dr_sw: endpoint {
+				remote-endpoint = <&usb1_drd_sw>;
+			};
+		};
+	};
+
+	rtc@68 {
+		compatible = "dallas,ds1337";
+		reg = <0x68>;
+		interrupt-parent = <&gpio2>;
+		interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_rtc>;
+		wakeup-source;
+	};
+};
+
+&lpi2c5 {
+	clock-frequency = <400000>;
+	pinctrl-names = "default", "sleep", "gpio";
+	pinctrl-0 = <&pinctrl_lpi2c5>;
+	pinctrl-1 = <&pinctrl_lpi2c5_gpio>;
+	pinctrl-2 = <&pinctrl_lpi2c5_gpio>;
+	scl-gpios = <&gpio2 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+	sda-gpios = <&gpio2 22 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+	status = "okay";
+};
+
+&lpi2c7 {
+	clock-frequency = <400000>;
+	pinctrl-names = "default", "sleep", "gpio";
+	pinctrl-0 = <&pinctrl_lpi2c7>;
+	pinctrl-1 = <&pinctrl_lpi2c7_gpio>;
+	pinctrl-2 = <&pinctrl_lpi2c7_gpio>;
+	scl-gpios = <&gpio2 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+	sda-gpios = <&gpio2 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+	status = "okay";
+
+	pca6408_1: gpio@20 {
+		compatible = "nxp,pcal6408";
+		reg = <0x20>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <10 IRQ_TYPE_LEVEL_LOW>;
+	};
+
+	pca6408_2: gpio@21 {
+		compatible = "nxp,pcal6408";
+		reg = <0x21>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <10 IRQ_TYPE_LEVEL_LOW>;
+	};
+
+	st33ktpm2xi2c: tpm@2e {
+		compatible = "st,st33ktpm2xi2c", "tcg,tpm-tis-i2c";
+		reg = <0x2e>;
+	};
+};
+
+&lpspi8 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_lpspi8>;
+	cs-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
+	status = "okay";
+
+	/* CAN controller */
+	can0: can@0 {
+		compatible = "microchip,mcp251xfd";
+		reg = <0>;
+		clocks = <&clk40m>;
+		interrupt-parent = <&gpio2>;
+		interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_can>;
+		spi-max-frequency = <1000000>;
+	};
+};
+
+/* Console (J10) */
+&lpuart1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart1>;
+	status = "okay";
+};
+
+/* Header (J12.4, J12.6) */
+&lpuart6 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart6>;
+	status = "okay";
+};
+
+/* Header (J12.11, J12.13) */
+&lpuart7 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart7>;
+	status = "okay";
+};
+
+&tpm3 {
+	pinctrl-0 = <&pinctrl_tpm3>;
+	pinctrl-names = "default";
+	status = "okay";
+};
+
+&usbotg1 {
+	dr_mode = "otg";
+	hnp-disable;
+	srp-disable;
+	adp-disable;
+	usb-role-switch;
+	disable-over-current;
+	samsung,picophy-pre-emp-curr-control = <3>;
+	samsung,picophy-dc-vol-level-adjust = <7>;
+	status = "okay";
+
+	port {
+		usb1_drd_sw: endpoint {
+			remote-endpoint = <&typec1_dr_sw>;
+		};
+	};
+};
+
+&usbotg2 {
+	disable-over-current;
+	dr_mode = "host";
+	status = "okay";
+};
+
+/* SD */
+&usdhc2 {
+	pinctrl-names = "default", "state_100mhz", "state_200mhz";
+	pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>;
+	pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>;
+	pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>;
+	cd-gpios = <&gpio3 0 GPIO_ACTIVE_LOW>;
+	vmmc-supply = <&reg_usdhc2_vmmc>;
+	bus-width = <4>;
+	no-sdio;
+	no-mmc;
+	status = "okay";
+};
+
+&wdog3 {
+	status = "okay";
+};
+
+&iomuxc {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_hog>;
+
+	pinctrl_can: cangrp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO03__GPIO2_IO03			0x31e
+		>;
+	};
+
+	pinctrl_captouch: captouchgrp {
+		fsl,pins = <
+			MX93_PAD_CCM_CLKO2__GPIO3_IO27			0x31e
+		>;
+	};
+
+	pinctrl_extcon: extcongrp {
+		fsl,pins = <
+			MX93_PAD_CCM_CLKO4__GPIO4_IO29			0x31e
+		>;
+	};
+
+	pinctrl_fec: fecgrp {
+		fsl,pins = <
+			MX93_PAD_ENET2_RD0__ENET1_RGMII_RD0		0x57e
+			MX93_PAD_ENET2_RD1__ENET1_RGMII_RD1		0x57e
+			MX93_PAD_ENET2_RD2__ENET1_RGMII_RD2		0x57e
+			MX93_PAD_ENET2_RD3__ENET1_RGMII_RD3		0x37e
+			MX93_PAD_ENET2_RXC__ENET1_RGMII_RXC		0x58e
+			MX93_PAD_ENET2_RX_CTL__ENET1_RGMII_RX_CTL	0x57e
+			MX93_PAD_ENET2_TD0__ENET1_RGMII_TD0		0x57e
+			MX93_PAD_ENET2_TD1__ENET1_RGMII_TD1		0x57e
+			MX93_PAD_ENET2_TD2__ENET1_RGMII_TD2		0x57e
+			MX93_PAD_ENET2_TD3__ENET1_RGMII_TD3		0x57e
+			MX93_PAD_ENET2_TXC__ENET1_RGMII_TXC		0x58e
+			MX93_PAD_ENET2_TX_CTL__ENET1_RGMII_TX_CTL	0x57e
+		>;
+	};
+
+	pinctrl_fec_sleep: fecsleepgrp {
+		fsl,pins = <
+			MX93_PAD_ENET2_RD0__GPIO4_IO24			0x51e
+			MX93_PAD_ENET2_RD1__GPIO4_IO25			0x51e
+			MX93_PAD_ENET2_RD2__GPIO4_IO26			0x51e
+			MX93_PAD_ENET2_RD3__GPIO4_IO27			0x51e
+			MX93_PAD_ENET2_RXC__GPIO4_IO23			0x51e
+			MX93_PAD_ENET2_RX_CTL__GPIO4_IO22		0x51e
+			MX93_PAD_ENET2_TD0__GPIO4_IO19			0x51e
+			MX93_PAD_ENET2_TD1__GPIO4_IO18			0x51e
+			MX93_PAD_ENET2_TD2__GPIO4_IO17			0x51e
+			MX93_PAD_ENET2_TD3__GPIO4_IO16			0x51e
+			MX93_PAD_ENET2_TXC__GPIO4_IO21			0x51e
+			MX93_PAD_ENET2_TX_CTL__GPIO4_IO20		0x51e
+		>;
+	};
+
+	pinctrl_flexcan1: flexcan1grp {
+		fsl,pins = <
+			MX93_PAD_PDM_CLK__CAN1_TX			0x139e
+			MX93_PAD_PDM_BIT_STREAM0__CAN1_RX		0x139e
+		>;
+	};
+
+	pinctrl_hog: hoggrp {
+		fsl,pins = <
+			/* GPIO Expanders shared IRQ */
+			MX93_PAD_PDM_BIT_STREAM1__GPIO1_IO10            0x31e
+		>;
+	};
+
+	pinctrl_leds_gpio: ledgrp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO11__GPIO2_IO11			0x31e
+		>;
+	};
+
+	pinctrl_lpi2c1: lpi2c1grp {
+		fsl,pins = <
+			MX93_PAD_I2C1_SCL__LPI2C1_SCL			0x40000b9e
+			MX93_PAD_I2C1_SDA__LPI2C1_SDA			0x40000b9e
+		>;
+	};
+
+	pinctrl_lpi2c1_gpio: lpi2c1-gpiogrp {
+		fsl,pins = <
+			MX93_PAD_I2C1_SCL__GPIO1_IO00			0x31e
+			MX93_PAD_I2C1_SDA__GPIO1_IO01			0x31e
+		>;
+	};
+
+	pinctrl_lpi2c5: lpi2c5grp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO22__LPI2C5_SDA			0x40000b9e
+			MX93_PAD_GPIO_IO23__LPI2C5_SCL			0x40000b9e
+		>;
+	};
+
+	pinctrl_lpi2c5_gpio: lpi2c5-gpiogrp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO22__GPIO2_IO22			0x31e
+			MX93_PAD_GPIO_IO23__GPIO2_IO23			0x31e
+		>;
+	};
+
+	pinctrl_lpi2c7: lpi2c7grp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO07__LPI2C7_SCL			0x40000b9e
+			MX93_PAD_GPIO_IO06__LPI2C7_SDA			0x40000b9e
+		>;
+	};
+
+	pinctrl_lpi2c7_gpio: lpi2c7-gpiogrp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO07__GPIO2_IO07			0x31e
+			MX93_PAD_GPIO_IO06__GPIO2_IO06			0x31e
+		>;
+	};
+
+	pinctrl_lpspi8: lpspi8grp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO00__GPIO2_IO00			0x31e
+			MX93_PAD_GPIO_IO01__GPIO2_IO01			0x31e
+			MX93_PAD_GPIO_IO12__GPIO2_IO12			0x31e
+			MX93_PAD_GPIO_IO13__LPSPI8_SIN			0x31e
+			MX93_PAD_GPIO_IO14__LPSPI8_SOUT			0x31e
+			MX93_PAD_GPIO_IO15__LPSPI8_SCK			0x31e
+		>;
+	};
+
+	pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp {
+		fsl,pins = <
+			MX93_PAD_CCM_CLKO3__GPIO4_IO28			0x31e
+		>;
+	};
+
+	pinctrl_rtc: rtcgrp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO02__GPIO2_IO02			0x31e
+		>;
+	};
+
+	pinctrl_tpm3: tpm3grp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO24__TPM3_CH3			0x51e
+		>;
+	};
+
+	pinctrl_uart1: uart1grp {
+		fsl,pins = <
+			MX93_PAD_UART1_RXD__LPUART1_RX			0x31e
+			MX93_PAD_UART1_TXD__LPUART1_TX			0x31e
+		>;
+	};
+
+	pinctrl_uart6: uart6grp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO05__LPUART6_RX			0x31e
+			MX93_PAD_GPIO_IO04__LPUART6_TX			0x31e
+		>;
+	};
+
+	pinctrl_uart7: uart7grp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO09__LPUART7_RX			0x31e
+			MX93_PAD_GPIO_IO08__LPUART7_TX			0x31e
+		>;
+	};
+
+	pinctrl_usdhc2: usdhc2grp {
+		fsl,pins = <
+			MX93_PAD_SD2_CLK__USDHC2_CLK			0x1582
+			MX93_PAD_SD2_CMD__USDHC2_CMD			0x40001382
+			MX93_PAD_SD2_DATA0__USDHC2_DATA0		0x40001382
+			MX93_PAD_SD2_DATA1__USDHC2_DATA1		0x40001382
+			MX93_PAD_SD2_DATA2__USDHC2_DATA2		0x40001382
+			MX93_PAD_SD2_DATA3__USDHC2_DATA3		0x40001382
+			MX93_PAD_SD2_VSELECT__USDHC2_VSELECT		0x51e
+		>;
+	};
+
+	pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp {
+		fsl,pins = <
+			MX93_PAD_SD2_CLK__USDHC2_CLK			0x158e
+			MX93_PAD_SD2_CMD__USDHC2_CMD			0x4000138e
+			MX93_PAD_SD2_DATA0__USDHC2_DATA0		0x4000138e
+			MX93_PAD_SD2_DATA1__USDHC2_DATA1		0x4000138e
+			MX93_PAD_SD2_DATA2__USDHC2_DATA2		0x4000138e
+			MX93_PAD_SD2_DATA3__USDHC2_DATA3		0x4000138e
+			MX93_PAD_SD2_VSELECT__USDHC2_VSELECT		0x51e
+		>;
+	};
+
+	pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp {
+		fsl,pins = <
+			MX93_PAD_SD2_CLK__USDHC2_CLK			0x15fe
+			MX93_PAD_SD2_CMD__USDHC2_CMD			0x400013fe
+			MX93_PAD_SD2_DATA0__USDHC2_DATA0		0x400013fe
+			MX93_PAD_SD2_DATA1__USDHC2_DATA1		0x400013fe
+			MX93_PAD_SD2_DATA2__USDHC2_DATA2		0x400013fe
+			MX93_PAD_SD2_DATA3__USDHC2_DATA3		0x400013fe
+			MX93_PAD_SD2_VSELECT__USDHC2_VSELECT		0x51e
+		>;
+	};
+
+	pinctrl_usdhc2_gpio: usdhc2gpiogrp {
+		fsl,pins = <
+			MX93_PAD_SD2_CD_B__GPIO3_IO00			0x31e
+		>;
+	};
+};
-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 2/3] arm64: dts: freescale: Add support for Variscite DART-MX93
From: Stefano Radaelli @ 2026-03-27  9:08 UTC (permalink / raw)
  To: linux-kernel, devicetree, imx, linux-arm-kernel
  Cc: pierluigi.p, Stefano Radaelli, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo, Dario Binacchi, Alexander Stein,
	Maud Spierings, Josua Mayer, Markus Niebel, Primoz Fiser,
	Francesco Dolcini
In-Reply-To: <cover.1774601806.git.stefano.r@variscite.com>

From: Stefano Radaelli <stefano.r@variscite.com>

Add device tree support for the Variscite DART-MX93 system on module.
This SOM is designed to be used with various carrier boards.

The module includes:
- NXP i.MX93 MPU processor
- Up to 2GB of LPDDR4 memory
- Up to 128GB of eMMC storage memory
- Integrated 10/100/1000 Mbps Ethernet Transceiver
- Codec audio WM8904
- WIFI6 dual-band 802.11ax/ac/a/b/g/n with optional 802.15.4 and Bluetooth

Only SOM-specific peripherals are enabled by default. Carrier board
specific interfaces are left disabled to be enabled in the respective
carrier board device trees.

Link: https://variscite.com/system-on-module-som/i-mx-9/i-mx-93/dart-mx93/
Signed-off-by: Stefano Radaelli <stefano.r@variscite.com>
---
v1->v2:
 - Remove clock-frequency property from eqos node

 .../boot/dts/freescale/imx93-var-dart.dtsi    | 461 ++++++++++++++++++
 1 file changed, 461 insertions(+)
 create mode 100644 arch/arm64/boot/dts/freescale/imx93-var-dart.dtsi

diff --git a/arch/arm64/boot/dts/freescale/imx93-var-dart.dtsi b/arch/arm64/boot/dts/freescale/imx93-var-dart.dtsi
new file mode 100644
index 000000000000..69495bb7fc9f
--- /dev/null
+++ b/arch/arm64/boot/dts/freescale/imx93-var-dart.dtsi
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Common dtsi for Variscite DART-MX93
+ *
+ * Link: https://variscite.com/system-on-module-som/i-mx-9/i-mx-93/dart-mx93/
+ *
+ * Copyright (C) 2026 Variscite Ltd. - https://www.variscite.com/
+ *
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/usb/pd.h>
+#include "imx93.dtsi"
+
+/ {
+	model = "Variscite DART-MX93 Module";
+	compatible = "variscite,var-dart-mx93", "fsl,imx93";
+
+	sound-wm8904 {
+		compatible = "simple-audio-card";
+		simple-audio-card,bitclock-master = <&codec_dai>;
+		simple-audio-card,format = "i2s";
+		simple-audio-card,frame-master = <&codec_dai>;
+		simple-audio-card,mclk-fs = <256>;
+		simple-audio-card,name = "wm8904-audio";
+		simple-audio-card,routing =
+			"Headphone Jack", "HPOUTL",
+			"Headphone Jack", "HPOUTR",
+			"IN2L", "Line In Jack",
+			"IN2R", "Line In Jack",
+			"IN1L", "Microphone Jack",
+			"IN1R", "Microphone Jack";
+		simple-audio-card,widgets =
+			"Microphone", "Microphone Jack",
+			"Headphone", "Headphone Jack",
+			"Line", "Line In Jack";
+
+		codec_dai: simple-audio-card,codec {
+			sound-dai = <&wm8904>;
+		};
+
+		simple-audio-card,cpu {
+			sound-dai = <&sai1>;
+		};
+	};
+
+	wifi_pwrseq: wifi-pwrseq {
+		compatible = "mmc-pwrseq-simple";
+		post-power-on-delay-ms = <100>;
+		power-off-delay-us = <10000>;
+		reset-gpios = <&gpio4 14 GPIO_ACTIVE_LOW>, /* WIFI_RESET */
+			      <&gpio3 7 GPIO_ACTIVE_LOW>; /* WIFI_PWR_EN */
+	};
+};
+
+&cm33 {
+	mbox-names = "tx", "rx", "rxdb";
+	mboxes = <&mu1 0 1>,
+		 <&mu1 1 1>,
+		 <&mu1 3 1>;
+	memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>,
+			<&vdev1vring0>, <&vdev1vring1>, <&rsc_table>;
+	status = "okay";
+};
+
+&eqos {
+	pinctrl-names = "default", "sleep";
+	pinctrl-0 = <&pinctrl_eqos>;
+	pinctrl-1 = <&pinctrl_eqos_sleep>;
+	/*
+	 * The required RGMII TX and RX 2ns delays are implemented directly
+	 * in hardware via passive delay elements on the SOM PCB.
+	 * No delay configuration is needed in software via PHY driver.
+	 */
+	phy-mode = "rgmii";
+	phy-handle = <&ethphy0>;
+	snps,clk-csr = <5>;
+	status = "okay";
+
+	mdio {
+		compatible = "snps,dwmac-mdio";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ethphy0: ethernet-phy@0 {
+			compatible = "ethernet-phy-ieee802.3-c22";
+			reg = <0>;
+			reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
+			reset-assert-us = <10000>;
+			reset-deassert-us = <100000>;
+		};
+	};
+};
+
+&lpi2c3 {
+	clock-frequency = <400000>;
+	pinctrl-names = "default", "sleep", "gpio";
+	pinctrl-0 = <&pinctrl_lpi2c3>;
+	pinctrl-1 = <&pinctrl_lpi2c3_gpio>;
+	pinctrl-2 = <&pinctrl_lpi2c3_gpio>;
+	scl-gpios = <&gpio2 29 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+	sda-gpios = <&gpio2 28 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+	status = "okay";
+
+	wm8904: audio-codec@1a {
+		compatible = "wlf,wm8904";
+		reg = <0x1a>;
+		#sound-dai-cells = <0>;
+		clocks = <&clk IMX93_CLK_SAI1_GATE>;
+		clock-names = "mclk";
+		AVDD-supply = <&buck5>;
+		CPVDD-supply = <&buck5>;
+		DBVDD-supply = <&buck4>;
+		DCVDD-supply = <&buck5>;
+		MICVDD-supply = <&buck5>;
+		wlf,drc-cfg-names = "default", "peaklimiter", "tradition",
+				    "soft", "music";
+		/*
+		 * Config registers per name, respectively:
+		 * KNEE_IP = 0,   KNEE_OP = 0,     HI_COMP = 1,   LO_COMP = 1
+		 * KNEE_IP = -24, KNEE_OP = -6,    HI_COMP = 1/4, LO_COMP = 1
+		 * KNEE_IP = -42, KNEE_OP = -3,    HI_COMP = 0,   LO_COMP = 1
+		 * KNEE_IP = -45, KNEE_OP = -9,    HI_COMP = 1/8, LO_COMP = 1
+		 * KNEE_IP = -30, KNEE_OP = -10.5, HI_COMP = 1/4, LO_COMP = 1
+		 */
+		wlf,drc-cfg-regs = /bits/ 16 <0x01af 0x3248 0x0000 0x0000>,
+				   /bits/ 16 <0x04af 0x324b 0x0010 0x0408>,
+				   /bits/ 16 <0x04af 0x324b 0x0028 0x0704>,
+				   /bits/ 16 <0x04af 0x324b 0x0018 0x078c>,
+				   /bits/ 16 <0x04af 0x324b 0x0010 0x050e>;
+		/* GPIO1 = DMIC_CLK, don't touch others */
+		wlf,gpio-cfg = <0x0018>, <0xffff>, <0xffff>, <0xffff>;
+		/* DMIC is connected to IN1L */
+		wlf,in1l-as-dmicdat1;
+	};
+
+	pmic@25 {
+		compatible = "nxp,pca9451a";
+		reg = <0x25>;
+
+		regulators {
+			buck1: BUCK1 {
+				regulator-name = "BUCK1";
+				regulator-min-microvolt = <650000>;
+				regulator-max-microvolt = <2237500>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-ramp-delay = <3125>;
+			};
+
+			buck2: BUCK2 {
+				regulator-name = "BUCK2";
+				regulator-min-microvolt = <600000>;
+				regulator-max-microvolt = <2187500>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-ramp-delay = <3125>;
+			};
+
+			buck4: BUCK4 {
+				regulator-name = "BUCK4";
+				regulator-min-microvolt = <600000>;
+				regulator-max-microvolt = <3400000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			buck5: BUCK5 {
+				regulator-name = "BUCK5";
+				regulator-min-microvolt = <600000>;
+				regulator-max-microvolt = <3400000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			buck6: BUCK6 {
+				regulator-name = "BUCK6";
+				regulator-min-microvolt = <600000>;
+				regulator-max-microvolt = <3400000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			ldo1: LDO1 {
+				regulator-name = "LDO1";
+				regulator-min-microvolt = <1600000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			ldo4: LDO4 {
+				regulator-name = "LDO4";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			ldo5: LDO5 {
+				regulator-name = "LDO5";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+		};
+	};
+};
+
+/* BT module */
+&lpuart5 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart5>, <&pinctrl_bt>;
+	uart-has-rtscts;
+	status = "okay";
+
+	bluetooth {
+		compatible = "nxp,88w8987-bt";
+	};
+};
+
+&mu1 {
+	status = "okay";
+};
+
+&mu2 {
+	status = "okay";
+};
+
+&sai1 {
+	pinctrl-names = "default", "sleep";
+	pinctrl-0 = <&pinctrl_sai1>;
+	pinctrl-1 = <&pinctrl_sai1_sleep>;
+	assigned-clocks = <&clk IMX93_CLK_SAI1>;
+	assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>;
+	assigned-clock-rates = <12288000>;
+	#sound-dai-cells = <0>;
+	fsl,sai-mclk-direction-output;
+	status = "okay";
+};
+
+/* eMMC */
+&usdhc1 {
+	pinctrl-names = "default", "state_100mhz", "state_200mhz";
+	pinctrl-0 = <&pinctrl_usdhc1>;
+	pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
+	pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
+	bus-width = <8>;
+	non-removable;
+	status = "okay";
+};
+
+/* WiFi */
+&usdhc3 {
+	pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep";
+	pinctrl-0 = <&pinctrl_usdhc3>, <&pinctrl_usdhc3_wlan>;
+	pinctrl-1 = <&pinctrl_usdhc3_100mhz>, <&pinctrl_usdhc3_wlan>;
+	pinctrl-2 = <&pinctrl_usdhc3_200mhz>, <&pinctrl_usdhc3_wlan>;
+	pinctrl-3 = <&pinctrl_usdhc3_sleep>, <&pinctrl_usdhc3_wlan>;
+	mmc-pwrseq = <&wifi_pwrseq>;
+	keep-power-in-suspend;
+	bus-width = <4>;
+	non-removable;
+	wakeup-source;
+	status = "okay";
+};
+
+&iomuxc {
+	pinctrl_bt: btgrp {
+		fsl,pins = <
+			MX93_PAD_ENET2_MDIO__GPIO4_IO15				0x51e
+		>;
+	};
+
+	pinctrl_eqos: eqosgrp {
+		fsl,pins = <
+			MX93_PAD_ENET1_MDC__ENET_QOS_MDC			0x57e
+			MX93_PAD_ENET1_MDIO__ENET_QOS_MDIO			0x57e
+			MX93_PAD_ENET1_RD0__ENET_QOS_RGMII_RD0			0x57e
+			MX93_PAD_ENET1_RD1__ENET_QOS_RGMII_RD1			0x57e
+			MX93_PAD_ENET1_RD2__ENET_QOS_RGMII_RD2			0x57e
+			MX93_PAD_ENET1_RD3__ENET_QOS_RGMII_RD3			0x57e
+			MX93_PAD_ENET1_RXC__CCM_ENET_QOS_CLOCK_GENERATE_RX_CLK	0x58e
+			MX93_PAD_ENET1_RX_CTL__ENET_QOS_RGMII_RX_CTL		0x57e
+			MX93_PAD_ENET1_TD0__ENET_QOS_RGMII_TD0			0x57e
+			MX93_PAD_ENET1_TD1__ENET_QOS_RGMII_TD1			0x57e
+			MX93_PAD_ENET1_TD2__ENET_QOS_RGMII_TD2			0x57e
+			MX93_PAD_ENET1_TD3__ENET_QOS_RGMII_TD3			0x57e
+			MX93_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK	0x58e
+			MX93_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL		0x57e
+			MX93_PAD_UART2_TXD__GPIO1_IO07				0x51e
+		>;
+	};
+
+	pinctrl_eqos_sleep: eqos-sleepgrp {
+		fsl,pins = <
+			MX93_PAD_ENET1_MDC__GPIO4_IO00				0x31e
+			MX93_PAD_ENET1_MDIO__GPIO4_IO01				0x31e
+			MX93_PAD_ENET1_RD0__GPIO4_IO10				0x31e
+			MX93_PAD_ENET1_RD1__GPIO4_IO11				0x31e
+			MX93_PAD_ENET1_RD2__GPIO4_IO12				0x31e
+			MX93_PAD_ENET1_RD3__GPIO4_IO13				0x31e
+			MX93_PAD_ENET1_RXC__GPIO4_IO09				0x31e
+			MX93_PAD_ENET1_RX_CTL__GPIO4_IO08			0x31e
+			MX93_PAD_ENET1_TD0__GPIO4_IO05				0x31e
+			MX93_PAD_ENET1_TD1__GPIO4_IO04				0x31e
+			MX93_PAD_ENET1_TD2__GPIO4_IO03				0x31e
+			MX93_PAD_ENET1_TD3__GPIO4_IO02				0x31e
+			MX93_PAD_ENET1_TXC__GPIO4_IO07				0x31e
+			MX93_PAD_ENET1_TX_CTL__GPIO4_IO06			0x31e
+		>;
+	};
+
+	pinctrl_lpi2c3: lpi2c3grp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO28__LPI2C3_SDA				0x40000b9e
+			MX93_PAD_GPIO_IO29__LPI2C3_SCL				0x40000b9e
+		>;
+	};
+
+	pinctrl_lpi2c3_gpio: lpi2c3gpiogrp {
+		fsl,pins = <
+			MX93_PAD_GPIO_IO28__GPIO2_IO28				0x40000b9e
+			MX93_PAD_GPIO_IO29__GPIO2_IO29				0x40000b9e
+		>;
+	};
+
+	pinctrl_sai1: sai1grp {
+		fsl,pins = <
+			MX93_PAD_SAI1_TXC__SAI1_TX_BCLK				0x31e
+			MX93_PAD_SAI1_TXFS__SAI1_TX_SYNC			0x31e
+			MX93_PAD_SAI1_TXD0__SAI1_TX_DATA00			0x31e
+			MX93_PAD_SAI1_RXD0__SAI1_RX_DATA00			0x31e
+			MX93_PAD_I2C2_SDA__SAI1_RX_BCLK				0x31e
+			MX93_PAD_I2C2_SCL__SAI1_RX_SYNC				0x31e
+			MX93_PAD_UART2_RXD__SAI1_MCLK				0x31e
+		>;
+	};
+
+	pinctrl_sai1_sleep: sai1-sleepgrp {
+		fsl,pins = <
+			MX93_PAD_SAI1_TXC__GPIO1_IO12				0x31e
+			MX93_PAD_SAI1_TXFS__GPIO1_IO11				0x31e
+			MX93_PAD_SAI1_TXD0__GPIO1_IO13				0x31e
+			MX93_PAD_SAI1_RXD0__GPIO1_IO14				0x31e
+			MX93_PAD_UART2_RXD__GPIO1_IO06				0x31e
+			MX93_PAD_I2C2_SDA__GPIO1_IO03				0x31e
+			MX93_PAD_I2C2_SCL__GPIO1_IO02				0x31e
+		>;
+	};
+
+	pinctrl_uart5: uart5grp {
+		fsl,pins = <
+			MX93_PAD_DAP_TDO_TRACESWO__LPUART5_TX			0x31e
+			MX93_PAD_DAP_TDI__LPUART5_RX				0x31e
+			MX93_PAD_DAP_TMS_SWDIO__LPUART5_RTS_B			0x31e
+			MX93_PAD_DAP_TCLK_SWCLK__LPUART5_CTS_B			0x31e
+		>;
+	};
+
+	pinctrl_usdhc1: usdhc1grp {
+		fsl,pins = <
+			MX93_PAD_SD1_CLK__USDHC1_CLK				0x1582
+			MX93_PAD_SD1_CMD__USDHC1_CMD				0x40001382
+			MX93_PAD_SD1_DATA0__USDHC1_DATA0			0x40001382
+			MX93_PAD_SD1_DATA1__USDHC1_DATA1			0x40001382
+			MX93_PAD_SD1_DATA2__USDHC1_DATA2			0x40001382
+			MX93_PAD_SD1_DATA3__USDHC1_DATA3			0x40001382
+			MX93_PAD_SD1_DATA4__USDHC1_DATA4			0x40001382
+			MX93_PAD_SD1_DATA5__USDHC1_DATA5			0x40001382
+			MX93_PAD_SD1_DATA6__USDHC1_DATA6			0x40001382
+			MX93_PAD_SD1_DATA7__USDHC1_DATA7			0x40001382
+			MX93_PAD_SD1_STROBE__USDHC1_STROBE			0x1582
+		>;
+	};
+
+	pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp {
+		fsl,pins = <
+			MX93_PAD_SD1_CLK__USDHC1_CLK				0x158e
+			MX93_PAD_SD1_CMD__USDHC1_CMD				0x4000138e
+			MX93_PAD_SD1_DATA0__USDHC1_DATA0			0x4000138e
+			MX93_PAD_SD1_DATA1__USDHC1_DATA1			0x4000138e
+			MX93_PAD_SD1_DATA2__USDHC1_DATA2			0x4000138e
+			MX93_PAD_SD1_DATA3__USDHC1_DATA3			0x4000138e
+			MX93_PAD_SD1_DATA4__USDHC1_DATA4			0x4000138e
+			MX93_PAD_SD1_DATA5__USDHC1_DATA5			0x4000138e
+			MX93_PAD_SD1_DATA6__USDHC1_DATA6			0x4000138e
+			MX93_PAD_SD1_DATA7__USDHC1_DATA7			0x4000138e
+			MX93_PAD_SD1_STROBE__USDHC1_STROBE			0x158e
+		>;
+	};
+
+	pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp {
+		fsl,pins = <
+			MX93_PAD_SD1_CLK__USDHC1_CLK				0x15fe
+			MX93_PAD_SD1_CMD__USDHC1_CMD				0x400013fe
+			MX93_PAD_SD1_DATA0__USDHC1_DATA0			0x400013fe
+			MX93_PAD_SD1_DATA1__USDHC1_DATA1			0x400013fe
+			MX93_PAD_SD1_DATA2__USDHC1_DATA2			0x400013fe
+			MX93_PAD_SD1_DATA3__USDHC1_DATA3			0x400013fe
+			MX93_PAD_SD1_DATA4__USDHC1_DATA4			0x400013fe
+			MX93_PAD_SD1_DATA5__USDHC1_DATA5			0x400013fe
+			MX93_PAD_SD1_DATA6__USDHC1_DATA6			0x400013fe
+			MX93_PAD_SD1_DATA7__USDHC1_DATA7			0x400013fe
+			MX93_PAD_SD1_STROBE__USDHC1_STROBE			0x15fe
+		>;
+	};
+
+	pinctrl_usdhc3: usdhc3grp {
+		fsl,pins = <
+			MX93_PAD_SD3_CLK__USDHC3_CLK				0x1582
+			MX93_PAD_SD3_CMD__USDHC3_CMD				0x40001382
+			MX93_PAD_SD3_DATA0__USDHC3_DATA0			0x40001382
+			MX93_PAD_SD3_DATA1__USDHC3_DATA1			0x40001382
+			MX93_PAD_SD3_DATA2__USDHC3_DATA2			0x40001382
+			MX93_PAD_SD3_DATA3__USDHC3_DATA3			0x40001382
+		>;
+	};
+
+	pinctrl_usdhc3_100mhz: usdhc3-100mhzgrp {
+		fsl,pins = <
+			MX93_PAD_SD3_CLK__USDHC3_CLK				0x158e
+			MX93_PAD_SD3_CMD__USDHC3_CMD				0x4000138e
+			MX93_PAD_SD3_DATA0__USDHC3_DATA0			0x4000138e
+			MX93_PAD_SD3_DATA1__USDHC3_DATA1			0x4000138e
+			MX93_PAD_SD3_DATA2__USDHC3_DATA2			0x4000138e
+			MX93_PAD_SD3_DATA3__USDHC3_DATA3			0x4000138e
+		>;
+	};
+
+	pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp {
+		fsl,pins = <
+			MX93_PAD_SD3_CLK__USDHC3_CLK				0x15fe
+			MX93_PAD_SD3_CMD__USDHC3_CMD				0x400013fe
+			MX93_PAD_SD3_DATA0__USDHC3_DATA0			0x400013fe
+			MX93_PAD_SD3_DATA1__USDHC3_DATA1			0x400013fe
+			MX93_PAD_SD3_DATA2__USDHC3_DATA2			0x400013fe
+			MX93_PAD_SD3_DATA3__USDHC3_DATA3			0x400013fe
+		>;
+	};
+
+	pinctrl_usdhc3_sleep: usdhc3-sleepgrp {
+		fsl,pins = <
+			MX93_PAD_SD3_CLK__GPIO3_IO20				0x400
+			MX93_PAD_SD3_CMD__GPIO3_IO21				0x400
+			MX93_PAD_SD3_DATA0__GPIO3_IO22				0x400
+			MX93_PAD_SD3_DATA1__GPIO3_IO23				0x400
+			MX93_PAD_SD3_DATA2__GPIO3_IO24				0x400
+			MX93_PAD_SD3_DATA3__GPIO3_IO25				0x400
+		>;
+	};
+
+	pinctrl_usdhc3_wlan: usdhc3wlangrp {
+		fsl,pins = <
+			MX93_PAD_ENET2_MDC__GPIO4_IO14				0x51e
+			MX93_PAD_SD2_RESET_B__GPIO3_IO07			0x51e
+		>;
+	};
+};
-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 1/3] dt-bindings: arm: fsl: add Variscite DART-MX93 Boards
From: Stefano Radaelli @ 2026-03-27  9:08 UTC (permalink / raw)
  To: linux-kernel, devicetree, imx, linux-arm-kernel
  Cc: pierluigi.p, Stefano Radaelli, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo, Dario Binacchi, Alexander Stein,
	Maud Spierings, Josua Mayer, Markus Niebel, Primoz Fiser,
	Francesco Dolcini, Conor Dooley
In-Reply-To: <cover.1774601806.git.stefano.r@variscite.com>

From: Stefano Radaelli <stefano.r@variscite.com>

Add DT compatible strings for Variscite DART-MX93 SoM and Variscite
development carrier Board.

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Stefano Radaelli <stefano.r@variscite.com>
---
v1->v2:
 - 

 Documentation/devicetree/bindings/arm/fsl.yaml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/fsl.yaml b/Documentation/devicetree/bindings/arm/fsl.yaml
index da2be7114f64..77497a261fd5 100644
--- a/Documentation/devicetree/bindings/arm/fsl.yaml
+++ b/Documentation/devicetree/bindings/arm/fsl.yaml
@@ -1587,6 +1587,12 @@ properties:
           - const: variscite,var-dart-mx91 # Variscite DART-MX91 SOM
           - const: fsl,imx91
 
+      - description: Variscite DART-MX93 based boards
+        items:
+          - const: variscite,var-dart-mx93-sonata # Variscite DART-MX93 on Sonata Development Board
+          - const: variscite,var-dart-mx93 # Variscite DART-MX93 SOM
+          - const: fsl,imx93
+
       - description: Variscite VAR-SOM-MX93 based boards
         items:
           - const: variscite,var-som-mx93-symphony
-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 0/3] Add support for Variscite DART-MX93 and Sonata board
From: Stefano Radaelli @ 2026-03-27  9:08 UTC (permalink / raw)
  To: linux-kernel, devicetree, imx, linux-arm-kernel
  Cc: pierluigi.p, Stefano Radaelli, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo, Dario Binacchi, Alexander Stein,
	Maud Spierings, Josua Mayer, Markus Niebel, Primoz Fiser,
	Francesco Dolcini

This patch series adds support for the Variscite DART-MX93 system on
module and the Sonata carrier board.

The series includes:
- SOM device tree with on-module peripherals
- Sonata carrier board device tree with board-specific features

The implementation follows the standard SOM + carrier board pattern
where the SOM dtsi contains only peripherals mounted on the module,
while carrier-specific interfaces are enabled in the board dts.

v1->v2:
 - Remove clock-frequency property from eqos node

Stefano Radaelli (3):
  dt-bindings: arm: fsl: add Variscite DART-MX93 Boards
  arm64: dts: freescale: Add support for Variscite DART-MX93
  arm64: dts: imx93-var-dart: Add support for Variscite Sonata board

 .../devicetree/bindings/arm/fsl.yaml          |   6 +
 arch/arm64/boot/dts/freescale/Makefile        |   1 +
 .../dts/freescale/imx93-var-dart-sonata.dts   | 654 ++++++++++++++++++
 .../boot/dts/freescale/imx93-var-dart.dtsi    | 461 ++++++++++++
 4 files changed, 1122 insertions(+)
 create mode 100644 arch/arm64/boot/dts/freescale/imx93-var-dart-sonata.dts
 create mode 100644 arch/arm64/boot/dts/freescale/imx93-var-dart.dtsi


base-commit: 6c4b48012d57f205c94f25da0c68a4027ea6daf6
-- 
2.47.3


^ permalink raw reply

* Re: [PATCH 2/4] arm64: dts: renesas: Fix missing cells and reg in Salvator-X panel DTO
From: Laurent Pinchart @ 2026-03-27  9:07 UTC (permalink / raw)
  To: Marek Vasut
  Cc: linux-arm-kernel, Conor Dooley, Geert Uytterhoeven,
	Krzysztof Kozlowski, Magnus Damm, Rob Herring, devicetree,
	linux-kernel, linux-renesas-soc
In-Reply-To: <20260326042411.215241-3-marek.vasut+renesas@mailbox.org>

Hi Marek,

Thank you for the patch.

On Thu, Mar 26, 2026 at 05:23:59AM +0100, Marek Vasut wrote:
> Add missing cells and reg DT property into Salvator-X panel DTO to fix
> the following warning:
> 
> "
> arch/arm64/boot/dts/renesas/salvator-panel-aa104xd12.dtso:30.10-34.5: Warning (unit_address_vs_reg): /fragment@2/__overlay__/ports/port@1: node has a unit name, but no reg or ranges property
> "
> 
> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

> ---
> Cc: Conor Dooley <conor+dt@kernel.org>
> Cc: Geert Uytterhoeven <geert+renesas@glider.be>
> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> Cc: Magnus Damm <magnus.damm@gmail.com>
> Cc: Rob Herring <robh@kernel.org>
> Cc: devicetree@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-renesas-soc@vger.kernel.org
> ---
>  arch/arm64/boot/dts/renesas/salvator-panel-aa104xd12.dtso | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/renesas/salvator-panel-aa104xd12.dtso b/arch/arm64/boot/dts/renesas/salvator-panel-aa104xd12.dtso
> index c83a30adc6adf..7807c3f80409a 100644
> --- a/arch/arm64/boot/dts/renesas/salvator-panel-aa104xd12.dtso
> +++ b/arch/arm64/boot/dts/renesas/salvator-panel-aa104xd12.dtso
> @@ -27,7 +27,12 @@ &lvds0 {
>  	status = "okay";
>  
>  	ports {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
>  		port@1 {
> +			reg = <1>;
> +
>  			lvds0_out: endpoint {
>  				remote-endpoint = <&panel_in>;
>  			};

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* Re: [PATCH 1/4] arm64: dts: renesas: Fix missing cells and reg in Draak/Ebisu panel DTO
From: Laurent Pinchart @ 2026-03-27  9:07 UTC (permalink / raw)
  To: Marek Vasut
  Cc: linux-arm-kernel, Conor Dooley, Geert Uytterhoeven,
	Krzysztof Kozlowski, Magnus Damm, Rob Herring, devicetree,
	linux-kernel, linux-renesas-soc
In-Reply-To: <20260326042411.215241-2-marek.vasut+renesas@mailbox.org>

On Thu, Mar 26, 2026 at 05:23:58AM +0100, Marek Vasut wrote:
> Add missing cells and reg DT property into Draak/Ebisu panel DTO to fix
> the following warning:
> 
> "
> arch/arm64/boot/dts/renesas/draak-ebisu-panel-aa104xd12.dtso:30.10-34.5: Warning (unit_address_vs_reg): /fragment@2/__overlay__/ports/port@1: node has a unit name, but no reg or ranges property
> "
> 
> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

It's slightly annoying to have to specify those properties in overlays.
It would be nice to improve the tooling, but a warning-free build is
probably a more important target for now.

Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

> ---
> Cc: Conor Dooley <conor+dt@kernel.org>
> Cc: Geert Uytterhoeven <geert+renesas@glider.be>
> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> Cc: Magnus Damm <magnus.damm@gmail.com>
> Cc: Rob Herring <robh@kernel.org>
> Cc: devicetree@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-renesas-soc@vger.kernel.org
> ---
>  arch/arm64/boot/dts/renesas/draak-ebisu-panel-aa104xd12.dtso | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/renesas/draak-ebisu-panel-aa104xd12.dtso b/arch/arm64/boot/dts/renesas/draak-ebisu-panel-aa104xd12.dtso
> index 258f8668ca361..90767d74e21b2 100644
> --- a/arch/arm64/boot/dts/renesas/draak-ebisu-panel-aa104xd12.dtso
> +++ b/arch/arm64/boot/dts/renesas/draak-ebisu-panel-aa104xd12.dtso
> @@ -27,7 +27,12 @@ &lvds1 {
>  	status = "okay";
>  
>  	ports {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
>  		port@1 {
> +			reg = <1>;
> +
>  			lvds1_out: endpoint {
>  				remote-endpoint = <&panel_in>;
>  			};

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* Re: [PATCH v5] arm64: dts: rockchip: rock-3b: Model PI6C20100 as gated-fixed-clock
From: Midgy Balon @ 2026-03-27  9:07 UTC (permalink / raw)
  To: Heiko Stuebner
  Cc: Jonas Karlman, linux-rockchip, shawn.lin, jonas, linux-arm-kernel,
	devicetree
In-Reply-To: <10821700.nUPlyArG6x@phil>

Hello Heiko,

I apologize for missing Jonas' comments. It will be done for the next
version as requested.
Indeed Claude works "well" :D . I'll be more careful next time.

Thanks,

Midgy

Le mar. 24 mars 2026 à 23:27, Heiko Stuebner <heiko@sntech.de> a écrit :
>
> Hi Jonas,
>
> Am Dienstag, 24. März 2026, 18:15:38 Mitteleuropäische Normalzeit schrieb Jonas Karlman:
> > Hi Heiko,
> >
> > On 3/24/2026 6:04 PM, Heiko Stuebner wrote:
> > >
> > > On Fri, 20 Mar 2026 10:44:41 +0100, MidG971 wrote:
> > >> The Radxa ROCK 3B uses a PI6C20100 PCIe reference clock buffer to
> > >> provide a 100MHz reference clock to the PCIe 3.0 PHY and controllers.
> > >> This chip is currently modeled only as a fixed regulator
> > >> (vcc3v3_pi6c_03), with no clock output representation.
> > >>
> > >> The PI6C20100 is a clock generator, not a power supply. Model it
> > >> properly as a gated-fixed-clock, following the pattern established
> > >> for the Rock 5 ITX and other boards with similar PCIe clock buffer
> > >> chips.
> > >>
> > >> [...]
> > >
> > > Applied, thanks!
> >
> > My comments from v3 [1] was not addressed in v4 och v5. E.g.
> > regulator-always-on/boot-on not being removed and redundant comments.
> >
> > [1] https://lore.kernel.org/all/fec0f25d-733a-4b6c-aef1-2ac51bd15798@kwiboo.se/
>
> thank you so much for noticing. Looks like that AI thing is
> working "well" ;-) .
>
> I've droped the patch and recreated the for-next branch now.
>
> @Midgy, please honor feedback in future revisions.
>
> Thanks
> Heiko
>
>

^ permalink raw reply

* RE: [PATCH v3 2/2] iio: dac: ad5706r: Add support for AD5706R DAC
From: Torreno, Alexis Czezar @ 2026-03-27  9:01 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Lars-Peter Clausen, Hennerich, Michael, Jonathan Cameron,
	David Lechner, Sa, Nuno, Andy Shevchenko, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-iio@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <acPRSzrHUtSEx-J3@ashevche-desk.local>

> >
> > To neatly summarize, my needs are: (in future patches) 1. SPI
> > read/write can have different frequencies and runtime changeable
> 
> How does it related to regmap? Is it dependent on the register?
> 
> > 2. SPI data bits needs to be exactly 8bits or 16bits depending on
> > register width
> 
> This is solved very easily with regmap, no problem at all (two regmaps with
> configuration for 8-bit and 16-bit registers), I believe we have even driver in
> kernel that does exactly this.
> 
> > 3. DAC Device reads SPI command bits [14:12] for communication, not
> > just chip select
> 
> Okay, but I'm not sure how this is a limitation...
> 
> > For regmap to be used
> > 1. regmap_config would need new read_speed and write_speed entries.
> > 2. val_bits must now be changeable depending on the need.
> > 3. I think the read/write_flag_mask can do this.
> >
> > 1) is relatively easy I think, but am not sure with 2) as it might
> > break other regmap core code that already assumes it to be fixed.
> > Feels like a lot of work for a niche amount of devices, I may still
> > lean on the opinion of keeping regmap as is.
> 
> Okay, I leave it to others, for the simplicity we can leave driver as is, but make
> sure you put the summary of this into the cover letter, so we will be crystal
> clear why regmap hasn't been chosen.
> 

With some discussion with Nuno, I get now that I could still use regmap but employ
a custom regmap_bus for my use case. Will do this on v4, I think I can still
satisfy my future requirements with it. Thanks!

Regards,
Alexis

^ permalink raw reply

* Re: [PATCH 0/4] arm64: dts: renesas: Fix missing cells and reg
From: Geert Uytterhoeven @ 2026-03-27  8:58 UTC (permalink / raw)
  To: Marek Vasut
  Cc: Marek Vasut, linux-arm-kernel, Conor Dooley, Krzysztof Kozlowski,
	Magnus Damm, Rob Herring, devicetree, linux-kernel,
	linux-renesas-soc, Laurent Pinchart, Niklas Söderlund
In-Reply-To: <2ad69ee6-9170-46dc-a963-71f327192bad@mailbox.org>

On Thu, 26 Mar 2026 at 21:40, Marek Vasut <marek.vasut@mailbox.org> wrote:
> On 3/26/26 11:07 AM, Geert Uytterhoeven wrote:
> > On Thu, 26 Mar 2026 at 05:24, Marek Vasut
> > <marek.vasut+renesas@mailbox.org> wrote:
> >> Add missing cells and reg DT property into DTOs to fix warnings like this:
> >>
> >> "
> >> arch/arm64/boot/dts/renesas/draak-ebisu-panel-aa104xd12.dtso:30.10-34.5: Warning (unit_address_vs_reg): /fragment@2/__overlay__/ports/port@1: node has a unit name, but no reg or ranges property
> >> "
> >
> > All of these are dtc W=1 warnings, right?
>
> Yes, I slowly started linting and validating the various DTs we have to
> remove leftover warnings.

As this is about display and camera, I am pulling in some multimedia guys.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* [PATCH v1] arm64: dts: qcom: hamoa-iot-som: Add firmware-name to QUPv3 nodes
From: Xueyao An @ 2026-03-27  8:53 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-arm-msm, devicetree, linux-kernel

Traditionally, firmware loading for Serial Engines (SE) in the QUP hardware
of Qualcomm SoCs has been managed by TrustZone (TZ). While this approach
ensures secure SE assignment and access control, it limits flexibility for
developers who need to enable various protocols on different SEs.

Add the firmware-name property to QUPv3 nodes in the device tree to enable
firmware loading from the Linux environment. Handle SE assignments and
access control permissions directly within Linux, removing the dependency
on TrustZone.

Signed-off-by: Xueyao An <xueyao.an@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/hamoa-iot-som.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/hamoa-iot-som.dtsi b/arch/arm64/boot/dts/qcom/hamoa-iot-som.dtsi
index 1a9e930c0496..c26eaf187b17 100644
--- a/arch/arm64/boot/dts/qcom/hamoa-iot-som.dtsi
+++ b/arch/arm64/boot/dts/qcom/hamoa-iot-som.dtsi
@@ -458,14 +458,20 @@ &pcie6a_phy {
 };
 
 &qupv3_0 {
+	firmware-name = "qcom/x1e80100/qupv3fw.elf";
+
 	status = "okay";
 };
 
 &qupv3_1 {
+	firmware-name = "qcom/x1e80100/qupv3fw.elf";
+
 	status = "okay";
 };
 
 &qupv3_2 {
+	firmware-name = "qcom/x1e80100/qupv3fw.elf";
+
 	status = "okay";
 };
 
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH v9 2/9] lib: vsprintf: export simple_strntoull() in a safe prototype
From: Petr Mladek @ 2026-03-27  8:45 UTC (permalink / raw)
  To: rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet, Andrew Morton, Steven Rostedt, Andy Shevchenko,
	Rasmus Villemoes, Sergey Senozhatsky, Shuah Khan
In-Reply-To: <20260320-adf41513-iio-driver-v9-2-132f0d076374@analog.com>

On Fri 2026-03-20 16:27:27, Rodrigo Alencar via B4 Relay wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> 
> Expose simple_strntoull(), by addressing its FIXME, i.e. its prototype is
> slightly changed so that -ERANGE or -EINVAL can be evaluated by the user.
> Flow of the function is not changed and error value is returned in the
> end. Unsafe internal wrapper is created to reduce amount of changes.
> 
> --- a/include/linux/kstrtox.h
> +++ b/include/linux/kstrtox.h
> @@ -148,4 +148,8 @@ extern long simple_strtol(const char *,char **,unsigned int);
>  extern unsigned long long simple_strtoull(const char *,char **,unsigned int);
>  extern long long simple_strtoll(const char *,char **,unsigned int);
>  
> +extern ssize_t __must_check simple_strntoull(const char *startp, const char **endp,
> +					     unsigned int base, size_t max_chars,
> +					     unsigned long long *res);

Sigh, naming is hard. I personally find it a bit confusing that the
name is too similar to the unsafe API.

IMHO, the semantic of the new API is closer to kstrtoull().
It just limits the size, so I would call it kstrntoull().

Also I would use int as the return parameter, see below.


>  #endif	/* _LINUX_KSTRTOX_H */
> diff --git a/lib/vsprintf.c b/lib/vsprintf.c
> index 800b8ac49f53..6fb880f4013b 100644
> --- a/lib/vsprintf.c
> +++ b/lib/vsprintf.c
> @@ -75,25 +75,66 @@ enum hash_pointers_policy {
>  };
>  static enum hash_pointers_policy hash_pointers_mode __initdata;
>  
> +/**
> + * simple_strntoull - convert a string to an unsigned long long with a character limit
> + *
> + * @startp: The start of the string
> + * @endp: A pointer to the end of the parsed string will be placed here

I would write:

  * @endp: A pointer to the end of the parsed string (output)

> + * @base: The number base to use
> + * @max_chars: The maximum number of characters to parse
> + * @res: Where to write the result of the conversion on success

Nit: I would omit "on success" *res value is set to 0 on failure.
     Instead, I would write:

  * @res: Result of the conversion (output)

> + *
> + * Returns amount of processed characters on success, -ERANGE on overflow and
> + * -EINVAL on parsing error.
> + */
>  noinline
> -static unsigned long long simple_strntoull(const char *startp, char **endp, unsigned int base, size_t max_chars)
> +ssize_t simple_strntoull(const char *startp, const char **endp,
> +			 unsigned int base, size_t max_chars,
> +			 unsigned long long *res)

It might be enoungh to use "int" for the return value. The number
of proceed characters is pretty limited by definition. And it
would be similar to vsnprintf(), kstrtoull(), ...

I guess that you wanted to match the "size_t max_chars" parameter.
It makes some sense as well.

Please, use "int" especially if we agreed to call the new API
kstrntoull().

>  {
>  	const char *cp;
> -	unsigned long long result = 0ULL;
>  	size_t prefix_chars;
>  	unsigned int rv;
> +	ssize_t ret;
>  
>  	cp = _parse_integer_fixup_radix(startp, &base);
>  	prefix_chars = cp - startp;
>  	if (prefix_chars < max_chars) {
> -		rv = _parse_integer_limit(cp, base, &result, max_chars - prefix_chars);
> -		/* FIXME */
> +		rv = _parse_integer_limit(cp, base, res, max_chars - prefix_chars);
> +		if (rv & KSTRTOX_OVERFLOW)
> +			ret = -ERANGE;
> +		else if (rv == 0)
> +			ret = -EINVAL;
> +		else
> +			ret = rv + prefix_chars;
>  		cp += (rv & ~KSTRTOX_OVERFLOW);
>  	} else {
>  		/* Field too short for prefix + digit, skip over without converting */
>  		cp = startp + max_chars;
> +		ret = -EINVAL;
> +		*res = 0ULL;
>  	}
>  
> +	if (endp)
> +		*endp = cp;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(simple_strntoull);
> +
> +/* unsafe_strntoull ignores simple_strntoull() return value and endp const qualifier */
> +inline
> +static unsigned long long unsafe_strntoull(const char *startp, char **endp,
> +					   unsigned int base, size_t max_chars)
> +{
> +	unsigned long long result;
> +	const char *cp;
> +
> +#pragma GCC diagnostic push
> +#pragma GCC diagnostic ignored "-Wunused-result"
> +	simple_strntoull(startp, &cp, base, max_chars, &result);
> +#pragma GCC diagnostic pop
> +
>  	if (endp)
>  		*endp = (char *)cp;

IMHO, we do not need local "cp". We could simply pass the endp
to the new simple_strntoull. Or do I miss anything?

Best Regards,
Petr

^ permalink raw reply

* Re: [PATCH 4/7] dt-bindings: leds: irled: ir-spi-led: Add new duty-cycle value
From: Sean Young @ 2026-03-27  8:41 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Biswapriyo Nath, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Lee Jones, Pavel Machek,
	Michael Turquette, Stephen Boyd, Martin Botka, linux-arm-msm,
	devicetree, linux-kernel, linux-leds, linux-clk,
	~postmarketos/upstreaming, phone-devel
In-Reply-To: <20260327-natural-wild-mongrel-5bcc43@quoll>

On Fri, Mar 27, 2026 at 08:51:18AM +0100, Krzysztof Kozlowski wrote:
> On Wed, Mar 25, 2026 at 06:07:27PM +0000, Biswapriyo Nath wrote:
> > 30 duty cycle for IR transmitter is used in Xiaomi Redmi Note 8 (ginkgo).
> > 
> > Signed-off-by: Biswapriyo Nath <nathbappai@gmail.com>
> > ---
> >  Documentation/devicetree/bindings/leds/irled/ir-spi-led.yaml | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/Documentation/devicetree/bindings/leds/irled/ir-spi-led.yaml b/Documentation/devicetree/bindings/leds/irled/ir-spi-led.yaml
> > index 72cadebf6e3..0297bfbb275 100644
> > --- a/Documentation/devicetree/bindings/leds/irled/ir-spi-led.yaml
> > +++ b/Documentation/devicetree/bindings/leds/irled/ir-spi-led.yaml
> > @@ -25,7 +25,7 @@ properties:
> >  
> >    duty-cycle:
> >      $ref: /schemas/types.yaml#/definitions/uint8
> > -    enum: [50, 60, 70, 75, 80, 90]
> > +    enum: [30, 50, 60, 70, 75, 80, 90]
> 
> Hm, why is this enum, instead of 1-99, in the first place?

Well in reality only a few different duty cycles are used by IR protocols.
30% is quite common so that should part of the list. 

Having said that a range of 1-99 would be nicer. Do we set this like so:

 - minimum: 1
 - maximum: 99

?

Thanks,

Sean

^ permalink raw reply

* Re: (subset) [PATCH v3 0/4] iio: adc: meson-saradc: add support for Meson S4
From: Neil Armstrong @ 2026-03-27  8:31 UTC (permalink / raw)
  To: khilman, martin.blumenstingl, jbrunet, jic23, dlechner, andy,
	Nick Xie
  Cc: krzk+dt, robh, conor+dt, linux-iio, linux-amlogic,
	linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260325070618.81955-1-nick@khadas.com>

Hi,

On Wed, 25 Mar 2026 15:06:14 +0800, Nick Xie wrote:
> This series adds support for the SARADC IP block found in the Amlogic
> Meson S4 (S905Y4) SoC and enables it for the Khadas VIM1S board to
> support the onboard ADC 'Function' key.
> 
> There are no known hardware differences between the SARADC on the S4
> and the previous G12A generation. The S4 bindings utilize a fallback
> to G12A, but a dedicated driver match entry is included to ensure the
> correct part name is exposed to the userspace ABI.
> 
> [...]

Thanks, Applied to https://git.kernel.org/pub/scm/linux/kernel/git/amlogic/linux.git (v7.1/arm64-dt)

[3/4] arm64: dts: amlogic: meson-s4: add internal SARADC controller
      https://git.kernel.org/amlogic/c/3b095e9b3674da0666aea3021167ca44097ed7fa
[4/4] arm64: dts: amlogic: meson-s4-s905y4-khadas-vim1s: add Function key support
      https://git.kernel.org/amlogic/c/97c4d4b7de6ef264fbd45300a168c2a0874d6c75

These changes has been applied on the intermediate git tree [1].

The v7.1/arm64-dt branch will then be sent via a formal Pull Request to the Linux SoC maintainers
for inclusion in their intermediate git branches in order to be sent to Linus during
the next merge window, or sooner if it's a set of fixes.

In the cases of fixes, those will be merged in the current release candidate
kernel and as soon they appear on the Linux master branch they will be
backported to the previous Stable and Long-Stable kernels [2].

The intermediate git branches are merged daily in the linux-next tree [3],
people are encouraged testing these pre-release kernels and report issues on the
relevant mailing-lists.

If problems are discovered on those changes, please submit a signed-off-by revert
patch followed by a corrective changeset.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/amlogic/linux.git
[2] https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
[3] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git

-- 
Neil


^ permalink raw reply

* [PATCH 2/2] arm64: dts: qcom: monaco-evk: enable UART6 for robot expansion board
From: Canfeng Zhuang @ 2026-03-27  8:31 UTC (permalink / raw)
  To: konradybcio, andersson
  Cc: robh, krzk+dt, conor+dt, linux-arm-msm, devicetree,
	linux-arm-kernel
In-Reply-To: <20260327083101.1343613-1-canfeng.zhuang@oss.qualcomm.com>

The monaco-evk mezzanine connector supports a robot expansion board that
requires UART6, which is currently disabled. This prevents the expansion
board from exchanging data and control commands.

Enable UART6 and assign the serial2 alias to provide stable device
enumeration for the expansion board.

Signed-off-by: Canfeng Zhuang <canfeng.zhuang@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/monaco-evk.dts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/monaco-evk.dts b/arch/arm64/boot/dts/qcom/monaco-evk.dts
index 565418b86b2a..0b26861eac02 100644
--- a/arch/arm64/boot/dts/qcom/monaco-evk.dts
+++ b/arch/arm64/boot/dts/qcom/monaco-evk.dts
@@ -21,6 +21,7 @@ aliases {
 		ethernet0 = &ethernet0;
 		i2c1 = &i2c1;
 		serial0 = &uart7;
+		serial2 = &uart6;
 	};
 
 	chosen {
@@ -572,6 +573,10 @@ qup_i2c15_default: qup-i2c15-state {
 	};
 };
 
+&uart6 {
+	status = "okay";
+};
+
 &uart7 {
 	status = "okay";
 };
-- 
2.34.1


^ permalink raw reply related

* [PATCH 1/2] arm64: dts: qcom: lemans-evk: enable UART0 for robot expansion board
From: Canfeng Zhuang @ 2026-03-27  8:31 UTC (permalink / raw)
  To: konradybcio, andersson
  Cc: robh, krzk+dt, conor+dt, linux-arm-msm, devicetree,
	linux-arm-kernel
In-Reply-To: <20260327083101.1343613-1-canfeng.zhuang@oss.qualcomm.com>

The lemans-evk mezzanine connector supports a robot expansion board that
requires UART0, which is currently disabled. This prevents the expansion
board from exchanging data and control commands.

Enable UART0 and assign the serial2 alias to provide stable device
enumeration for the expansion board.

Signed-off-by: Canfeng Zhuang <canfeng.zhuang@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/lemans-evk.dts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/lemans-evk.dts b/arch/arm64/boot/dts/qcom/lemans-evk.dts
index 90fce947ca7e..1e9386677bf5 100644
--- a/arch/arm64/boot/dts/qcom/lemans-evk.dts
+++ b/arch/arm64/boot/dts/qcom/lemans-evk.dts
@@ -21,6 +21,7 @@ aliases {
 		ethernet0 = &ethernet0;
 		mmc1 = &sdhc;
 		serial0 = &uart10;
+		serial2 = &uart0;
 	};
 
 	dmic: audio-codec-0 {
@@ -870,6 +871,10 @@ usb_id: usb-id-state {
 	};
 };
 
+&uart0 {
+	status = "okay";
+};
+
 &uart10 {
 	compatible = "qcom,geni-debug-uart";
 	pinctrl-0 = <&qup_uart10_default>;
-- 
2.34.1


^ permalink raw reply related

* [PATCH 0/2] arm64: dts: qcom: enable UARTs for robot expansion board
From: Canfeng Zhuang @ 2026-03-27  8:30 UTC (permalink / raw)
  To: konradybcio, andersson
  Cc: robh, krzk+dt, conor+dt, linux-arm-msm, devicetree,
	linux-arm-kernel

The Qualcomm Lemans EVK and Monaco EVK boards expose a mezzanine
connector used by a motor control expansion board.

This expansion board hosts an MCU running NuttX and communicates with
Linux over UART, with all protocol handling done in userspace.

This series enables the required UARTs and assigns stable serial aliases
to ensure consistent device enumeration across platforms.

Canfeng Zhuang (2):
  arm64: dts: qcom: lemans-evk: enable UART0 for robot expansion board
  arm64: dts: qcom: monaco-evk: enable UART6 for robot expansion board

 arch/arm64/boot/dts/qcom/lemans-evk.dts | 5 +++++
 arch/arm64/boot/dts/qcom/monaco-evk.dts | 5 +++++
 2 files changed, 10 insertions(+)


base-commit: 46b513250491a7bfc97d98791dbe6a10bcc8129d
prerequisite-patch-id: a877aad0127409ffea7c0a7411968d336497b035
-- 
2.34.1


^ permalink raw reply

* Re: [PATCH 3/6] dt-bindings: phy: realtek,usb2phy.yaml: extend for resets and RTL9607C support
From: Krzysztof Kozlowski @ 2026-03-27  8:28 UTC (permalink / raw)
  To: Rustam Adilov
  Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Stanley Chang, linux-phy, devicetree, linux-kernel
In-Reply-To: <20260326193419.48419-4-adilov@disroot.org>

On Fri, Mar 27, 2026 at 12:34:16AM +0500, Rustam Adilov wrote:
>  description: |
> -  Realtek USB 2.0 PHY support the digital home center (DHC) RTD series SoCs.
> +  Realtek USB 2.0 PHY support the digital home center (DHC) RTD and
> +  RTL9607C series SoCs.
>    The USB 2.0 PHY driver is designed to support the XHCI controller. The SoCs
>    support multiple XHCI controllers. One PHY device node maps to one XHCI
>    controller.
> +  This driver also supports the OCHI and EHCI controllers.

Hardware is fixed, does not change. Don't reference your driver changes
here.

>  
>    RTD1295/RTD1619 SoCs USB
>    The USB architecture includes three XHCI controllers.
> @@ -57,6 +59,12 @@ description: |
>    XHCI controller#1 -- usb2phy -- phy#0
>    XHCI controller#2 -- usb2phy -- phy#0
>  
> +  RTL9607C SoCs USB
> +  The USB architecture includes OHCI and EHCI controllers.
> +  Both of them map to one USB2.0 PHY.
> +  OHCI controller#0 -- usb2phy -- phy#0
> +  EHCI controller#0 -- usb2phy -- phy#0
> +
>  properties:
>    compatible:
>      enum:
> @@ -69,6 +77,7 @@ properties:
>        - realtek,rtd1395-usb2phy-2port
>        - realtek,rtd1619-usb2phy
>        - realtek,rtd1619b-usb2phy
> +      - realtek,rtl9607-usb2phy
>  
>    reg:
>      items:
> @@ -130,6 +139,9 @@ properties:
>      minimum: -8
>      maximum: 8
>  
> +  resets:
> +    maxItems: 1
> +
>  required:
>    - compatible
>    - reg
> @@ -157,6 +169,15 @@ allOf:
>      then:
>        properties:
>          realtek,driving-level-compensate: false
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            enum:
> +              - realtek,rtl9607-usb2phy
> +    then:
> +      required:
> +        - resets

If it is unclear, they might not have it so,

else: ... :false

see example-schema.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH 01/12] dt-bindings: i3c: Add mipi-i3c-static-method to support SETAASA
From: Alexandre Belloni @ 2026-03-27  8:27 UTC (permalink / raw)
  To: Akhil R
  Cc: Frank.Li, acpica-devel, conor+dt, conor, devicetree, ebiggers,
	fredrik.markstrom, jonathanh, krzk+dt, lenb, linux-acpi,
	linux-hwmon, linux-i3c, linux-kernel, linux-tegra, linux,
	miquel.raynal, p.zabel, rafael, robert.moore, robh, smangipudi,
	thierry.reding
In-Reply-To: <20260327081858.32354-1-akhilrajeev@nvidia.com>

On 27/03/2026 13:48:58+0530, Akhil R wrote:
> On Thu, 26 Mar 2026 16:44:31 +0100, Alexandre Belloni wrote:
> > On 26/03/2026 10:05:03-0500, Rob Herring wrote:
> >> On Wed, Mar 18, 2026 at 05:31:50PM +0000, Conor Dooley wrote:
> >> > On Wed, Mar 18, 2026 at 10:57:14PM +0530, Akhil R wrote:
> >> > > Add the 'mipi-i3c-static-method' property mentioned in the MIPI I3C
> >> > > Discovery and Configuration Specification [1] to specify which discovery
> >> > > method an I3C device supports during bus initialization. The property is
> >> > > a bitmap, where a bit value of 1 indicates support for that method, and 0
> >> > > indicates lack of support.
> >> > > Bit 0: SETDASA CCC (Direct)
> >> > > Bit 1: SETAASA CCC (Broadcast)
> >> > > Bit 2: Other CCC (vendor / standards extension)
> >> > > All other bits are reserved.
> >> > > 
> >> > > It is specifically needed when an I3C device requires SETAASA for the
> >> > > address assignment. SETDASA will be supported by default if this property
> >> > > is absent - which means for now the property just serves as a flag to
> >> > > enable SETAASA, but keep the property as a bitmap to align with the
> >> > > specifications.
> >> > > 
> >> > > [1] https://www.mipi.org/specifications/disco
> >> > > 
> >> > > Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> >> > > ---
> >> > >  .../devicetree/bindings/i3c/i3c.yaml          | 30 ++++++++++++++++---
> >> > >  1 file changed, 26 insertions(+), 4 deletions(-)
> >> > > 
> >> > > diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml
> >> > > index e25fa72fd785..1705d90d4d79 100644
> >> > > --- a/Documentation/devicetree/bindings/i3c/i3c.yaml
> >> > > +++ b/Documentation/devicetree/bindings/i3c/i3c.yaml
> >> > > @@ -31,10 +31,12 @@ properties:
> >> > >        described in the device tree, which in turn means we have to describe
> >> > >        I3C devices.
> >> > >  
> >> > > -      Another use case for describing an I3C device in the device tree is when
> >> > > -      this I3C device has a static I2C address and we want to assign it a
> >> > > -      specific I3C dynamic address before the DAA takes place (so that other
> >> > > -      devices on the bus can't take this dynamic address).
> >> > > +      Other use-cases for describing an I3C device in the device tree are:
> >> > > +      - When the I3C device has a static I2C address and we want to assign
> >> > > +        it a specific I3C dynamic address before the DAA takes place (so
> >> > > +        that other devices on the bus can't take this dynamic address).
> >> > > +      - When the I3C device requires SETAASA for its discovery and uses a
> >> > > +        pre-defined static address.
> >> > >  
> >> > >    "#size-cells":
> >> > >      const: 0
> >> > > @@ -147,6 +149,26 @@ patternProperties:
> >> > >            through SETDASA. If static address is not present, this address is assigned
> >> > >            through SETNEWDA after assigning a temporary address via ENTDAA.
> >> > >  
> >> > > +      mipi-i3c-static-method:
> >> > > +        $ref: /schemas/types.yaml#/definitions/uint32
> >> > > +        minimum: 0x1
> >> > > +        maximum: 0xff
> >> > > +        default: 1
> >> > > +        description: |
> >> > > +          Bitmap describing which methods of Dynamic Address Assignment from a
> >> > > +          static address are supported by this I3C Target. A bit value of 1
> >> > > +          indicates support for that method, and 0 indicates lack of support.
> >> > 
> >> > I really am not keen on properties that are bitmaps, why can't we just
> >> > use the strings "setdasa", "setaasa" etc?
> >> 
> >> If this comes from a specification, then I'd tend to just copy it rather 
> >> than invent our own thing. Obviously if is something structured 
> >> fundamentally different from how DT is designed, then we wouldn't. But 
> >> this is just a simple property.
> >> 
> > 
> > The issue being that the specification is not public so it is difficult
> > to take any decision.
> 
> There is a public version available in the same link, but you would still
> have to provide them a name and an email ID. The document will be sent to
> the mail ID.
> 

The public version only contains one property:
mipi-disco-interface-revision


-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: pwm: amlogic: Add new bindings for S6 S7 S7D
From: Krzysztof Kozlowski @ 2026-03-27  8:25 UTC (permalink / raw)
  To: Xianwei Zhao
  Cc: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Heiner Kallweit, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, linux-pwm, devicetree,
	linux-kernel, linux-arm-kernel, linux-amlogic, Junyi Zhao
In-Reply-To: <20260326-s6-s7-pwm-v1-1-67e2f72b98bc@amlogic.com>

On Thu, Mar 26, 2026 at 06:35:38AM +0000, Xianwei Zhao wrote:
> +      - items:
> +          - enum:
> +              - amlogic,s6-pwm
> +              - amlogic,s7d-pwm
> +          - const: amlogic,s7-pwm
>        - items:
>            - enum:
>                - amlogic,meson8b-pwm-v2
> @@ -146,6 +152,20 @@ allOf:
>          clock-names: false
>        required:
>          - clocks
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            enum:
> +              - amlogic,s7-pwm
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: input clock of PWM

so simpler: "maxItems: 1"


> +        clock-names: false
> +      required:
> +        - clocks
>  
>    - if:
>        properties:
> @@ -182,3 +202,10 @@ examples:
>        clocks = <&pwm_src_a>, <&pwm_src_b>;
>        #pwm-cells = <3>;
>      };
> +  - |
> +    pwm@1000 {
> +      compatible = "amlogic,s7-pwm";

You already have three examples, don't add more.

With these changes:

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof


^ permalink raw reply

* [PATCH v2] dt-bindings: mfd: twl: Reference converted schemas for subnodes
From: Jihed Chaibi @ 2026-03-27  8:23 UTC (permalink / raw)
  To: andreas
  Cc: lee, robh, krzk+dt, conor+dt, devicetree, linux-kernel,
	jihed.chaibi.dev

Now that all TWL subnode bindings (audio, keypad, twl4030-usb, gpio,
usb-comparator) have been converted and merged into mainline, update the
parent ti,twl.yaml to properly reference them via $ref.

Replace the inline compatible definitions with $ref to the appropriate
schemas and unevaluatedProperties: false, matching the pattern already
used by the madc, gpadc, and charger subnodes.

Signed-off-by: Jihed Chaibi <jihed.chaibi.dev@gmail.com>
---
Changes in v2:
- Drop "YAML" from subject and commit message
- Remove incorrect claim that subnode properties were not being validated

 .../devicetree/bindings/mfd/ti,twl.yaml       | 50 ++++---------------
 1 file changed, 10 insertions(+), 40 deletions(-)

diff --git a/Documentation/devicetree/bindings/mfd/ti,twl.yaml b/Documentation/devicetree/bindings/mfd/ti,twl.yaml
index 9cc3e4721612..a3af8e717ec7 100644
--- a/Documentation/devicetree/bindings/mfd/ti,twl.yaml
+++ b/Documentation/devicetree/bindings/mfd/ti,twl.yaml
@@ -265,47 +265,23 @@ properties:

   audio:
     type: object
-    additionalProperties: true
-
-    properties:
-      compatible:
-        const: ti,twl4030-audio
-
-    required:
-      - compatible
+    $ref: /schemas/sound/ti,twl4030-audio.yaml
+    unevaluatedProperties: false

   keypad:
     type: object
-    additionalProperties: true
-
-    properties:
-      compatible:
-        const: ti,twl4030-keypad
-
-    required:
-      - compatible
+    $ref: /schemas/input/ti,twl4030-keypad.yaml
+    unevaluatedProperties: false

   twl4030-usb:
     type: object
-    additionalProperties: true
-
-    properties:
-      compatible:
-        const: ti,twl4030-usb
-
-    required:
-      - compatible
+    $ref: /schemas/usb/ti,twl4030-usb.yaml
+    unevaluatedProperties: false

   gpio:
     type: object
-    additionalProperties: true
-
-    properties:
-      compatible:
-        const: ti,twl4030-gpio
-
-    required:
-      - compatible
+    $ref: /schemas/gpio/ti,twl4030-gpio.yaml
+    unevaluatedProperties: false

   power:
     type: object
@@ -371,14 +347,8 @@ properties:

   usb-comparator:
     type: object
-    additionalProperties: true
-
-    properties:
-      compatible:
-        const: ti,twl6030-usb
-
-    required:
-      - compatible
+    $ref: /schemas/usb/ti,twl6030-usb.yaml
+    unevaluatedProperties: false

   pwm:
     type: object
--
2.47.3


^ permalink raw reply related

* Re: [PATCH 01/12] dt-bindings: i3c: Add mipi-i3c-static-method to support SETAASA
From: Akhil R @ 2026-03-27  8:18 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: Frank.Li, acpica-devel, akhilrajeev, conor+dt, conor, devicetree,
	ebiggers, fredrik.markstrom, jonathanh, krzk+dt, lenb, linux-acpi,
	linux-hwmon, linux-i3c, linux-kernel, linux-tegra, linux,
	miquel.raynal, p.zabel, rafael, robert.moore, robh, smangipudi,
	thierry.reding
In-Reply-To: <202603261544313f63018c@mail.local>

On Thu, 26 Mar 2026 16:44:31 +0100, Alexandre Belloni wrote:
> On 26/03/2026 10:05:03-0500, Rob Herring wrote:
>> On Wed, Mar 18, 2026 at 05:31:50PM +0000, Conor Dooley wrote:
>> > On Wed, Mar 18, 2026 at 10:57:14PM +0530, Akhil R wrote:
>> > > Add the 'mipi-i3c-static-method' property mentioned in the MIPI I3C
>> > > Discovery and Configuration Specification [1] to specify which discovery
>> > > method an I3C device supports during bus initialization. The property is
>> > > a bitmap, where a bit value of 1 indicates support for that method, and 0
>> > > indicates lack of support.
>> > > Bit 0: SETDASA CCC (Direct)
>> > > Bit 1: SETAASA CCC (Broadcast)
>> > > Bit 2: Other CCC (vendor / standards extension)
>> > > All other bits are reserved.
>> > > 
>> > > It is specifically needed when an I3C device requires SETAASA for the
>> > > address assignment. SETDASA will be supported by default if this property
>> > > is absent - which means for now the property just serves as a flag to
>> > > enable SETAASA, but keep the property as a bitmap to align with the
>> > > specifications.
>> > > 
>> > > [1] https://www.mipi.org/specifications/disco
>> > > 
>> > > Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>> > > ---
>> > >  .../devicetree/bindings/i3c/i3c.yaml          | 30 ++++++++++++++++---
>> > >  1 file changed, 26 insertions(+), 4 deletions(-)
>> > > 
>> > > diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml
>> > > index e25fa72fd785..1705d90d4d79 100644
>> > > --- a/Documentation/devicetree/bindings/i3c/i3c.yaml
>> > > +++ b/Documentation/devicetree/bindings/i3c/i3c.yaml
>> > > @@ -31,10 +31,12 @@ properties:
>> > >        described in the device tree, which in turn means we have to describe
>> > >        I3C devices.
>> > >  
>> > > -      Another use case for describing an I3C device in the device tree is when
>> > > -      this I3C device has a static I2C address and we want to assign it a
>> > > -      specific I3C dynamic address before the DAA takes place (so that other
>> > > -      devices on the bus can't take this dynamic address).
>> > > +      Other use-cases for describing an I3C device in the device tree are:
>> > > +      - When the I3C device has a static I2C address and we want to assign
>> > > +        it a specific I3C dynamic address before the DAA takes place (so
>> > > +        that other devices on the bus can't take this dynamic address).
>> > > +      - When the I3C device requires SETAASA for its discovery and uses a
>> > > +        pre-defined static address.
>> > >  
>> > >    "#size-cells":
>> > >      const: 0
>> > > @@ -147,6 +149,26 @@ patternProperties:
>> > >            through SETDASA. If static address is not present, this address is assigned
>> > >            through SETNEWDA after assigning a temporary address via ENTDAA.
>> > >  
>> > > +      mipi-i3c-static-method:
>> > > +        $ref: /schemas/types.yaml#/definitions/uint32
>> > > +        minimum: 0x1
>> > > +        maximum: 0xff
>> > > +        default: 1
>> > > +        description: |
>> > > +          Bitmap describing which methods of Dynamic Address Assignment from a
>> > > +          static address are supported by this I3C Target. A bit value of 1
>> > > +          indicates support for that method, and 0 indicates lack of support.
>> > 
>> > I really am not keen on properties that are bitmaps, why can't we just
>> > use the strings "setdasa", "setaasa" etc?
>> 
>> If this comes from a specification, then I'd tend to just copy it rather 
>> than invent our own thing. Obviously if is something structured 
>> fundamentally different from how DT is designed, then we wouldn't. But 
>> this is just a simple property.
>> 
> 
> The issue being that the specification is not public so it is difficult
> to take any decision.

There is a public version available in the same link, but you would still
have to provide them a name and an email ID. The document will be sent to
the mail ID.

Regards,
Akhil

^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: thermal: st,thermal-spear1340: convert to dtschema
From: Krzysztof Kozlowski @ 2026-03-27  8:06 UTC (permalink / raw)
  To: Gopi Krishna Menon
  Cc: rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh, krzk+dt,
	vireshk, conor+dt, linux-pm, devicetree, linux-kernel,
	linux-arm-kernel, soc, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry
In-Reply-To: <20260324-belligerent-armadillo-of-camouflage-e52f7b@quoll>

On 24/03/2026 10:30, Krzysztof Kozlowski wrote:
> On Mon, Mar 23, 2026 at 07:08:08PM +0530, Gopi Krishna Menon wrote:
>> +properties:
>> +  compatible:
>> +    const: st,thermal-spear1340
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  st,thermal-flags:
>> +    description: flags used to enable thermal sensor
>> +    $ref: /schemas/types.yaml#/definitions/uint32
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - st,thermal-flags
>> +
>> +unevaluatedProperties: false
> 
> additionalProperties: true

Thanks for pinging me on IRC about this - it should be:

additionalProperties: false

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v9 1/2] dt-bindings: display: Add ITE IT61620 MIPI DSI to HDMI bridge
From: Pet Weng @ 2026-03-27  8:02 UTC (permalink / raw)
  To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: dri-devel, devicetree, linux-kernel, Hermes Wu, Kenneth Hung,
	Pet Weng, Jau-chih Tseng, Pin-yen Lin, Krzysztof Kozlowski
In-Reply-To: <20260327-it61620-0714-v9-0-032938cb9d85@ite.com.tw>

This chip receives MIPI DSI input and outputs HDMI, and is commonly
connected to SoCs via I2C and DSI.

IT61620 is a variant of IT6162.
The main differences are listed below
- IT61620 supports only a single MIPI DSI input port, while
  IT6162 supports dual MIPI DSI ports.
- IT61620 does not include the internal MCU present in IT6162.

Because of these architectural differences, IT61620 uses a separate
compatible string even though the external interfaces and bindings
are largely similar.

Signed-off-by: Pet Weng <pet.weng@ite.com.tw>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
 .../bindings/display/bridge/ite,it61620.yaml       | 152 +++++++++++++++++++++
 1 file changed, 152 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/bridge/ite,it61620.yaml b/Documentation/devicetree/bindings/display/bridge/ite,it61620.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d4f0edeecd0e22f3fa92c9f8e276ce0806c24790
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/ite,it61620.yaml
@@ -0,0 +1,152 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/ite,it61620.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ITE IT61620 MIPI DSI to HDMI Bridge
+
+maintainers:
+  - Pet Weng <pet.weng@ite.com.tw>
+
+description: |
+  The ITE IT61620 is a high-performance, low-power HDMI bridge that converts
+  MIPI DSI input to HDMI 1.4b TMDS output. It supports up to 4 lanes of MIPI
+  D-PHY 2.0 input at 2.5Gbps per lane (10Gbps total), compatible with DSI-2
+  v2.0.
+
+  The HDMI transmitter side supports up to 4Kx2K@30Hz resolutions, and is
+  compliant with HDMI 1.4b and HDCP 1.4.
+
+  For audio, the IT61620 supports up to 8-channel LPCM via I2S (multi-line or
+  TDM mode), with optional S/PDIF or DSD (for SACD). It supports audio
+  sampling rates up to 192kHz.
+
+  IT61620 is a variant of IT6162.
+  The main differences are listed below
+  - IT61620 supports only a single MIPI DSI input port, while IT6162 supports
+    dual MIPI DSI ports.
+  - IT61620 does not include the internal MCU present in IT6162.
+
+  Because of these architectural differences, IT61620 uses a separate
+  compatible string even though the external interfaces and bindings are
+  largely similar.
+
+allOf:
+  - $ref: /schemas/sound/dai-common.yaml#
+
+properties:
+  compatible:
+    const: ite,it61620
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  ivdd-supply:
+    description: core voltage
+
+  ovdd-supply:
+    description: I/O voltage
+
+  ovdd1833-supply:
+    description: flexible I/O voltage
+
+  "#sound-dai-cells":
+    const: 0
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/$defs/port-base
+        unevaluatedProperties: false
+        description: Input port for MIPI DSI
+
+        properties:
+          endpoint:
+            $ref: /schemas/media/video-interfaces.yaml#
+            unevaluatedProperties: false
+            required:
+              - data-lanes
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output port for HDMI output
+
+      port@2:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Audio input port (I2S)
+
+    required:
+      - port@0
+      - port@1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - reset-gpios
+  - ivdd-supply
+  - ovdd-supply
+  - ovdd1833-supply
+  - ports
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        bridge@58 {
+            compatible = "ite,it61620";
+            reg = <0x58>;
+            #sound-dai-cells = <0>;
+            interrupt-parent = <&pio>;
+            interrupts = <128 IRQ_TYPE_LEVEL_LOW>;
+            pinctrl-names = "default";
+            pinctrl-0 = <&it61620_pins>;
+            reset-gpios = <&pio 127 GPIO_ACTIVE_LOW>;
+            ivdd-supply = <&pp1000_hdmi_x>;
+            ovdd-supply = <&pp3300_vio28_x>;
+            ovdd1833-supply = <&pp1800_vcamio_x>;
+
+            ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@0 {
+                    reg = <0>;
+                    it61620_dsi_in: endpoint {
+                        data-lanes = <0 1 2 3>;
+                        remote-endpoint = <&dsi_out>;
+                    };
+                };
+
+                port@1 {
+                    reg = <1>;
+                    it61620_hdmi_out: endpoint {
+                        remote-endpoint = <&hdmi_connector_in>;
+                    };
+                };
+
+                port@2 {
+                    reg = <2>;
+                    it61620_audio_in: endpoint {
+                        remote-endpoint = <&i2s0_out>;
+                    };
+                };
+            };
+        };
+    };

-- 
2.34.1


^ permalink raw reply related

* [PATCH v9 2/2] drm/bridge: Add ITE IT61620 MIPI DSI to HDMI bridge driver
From: Pet Weng @ 2026-03-27  8:02 UTC (permalink / raw)
  To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: dri-devel, devicetree, linux-kernel, Hermes Wu, Kenneth Hung,
	Pet Weng, Jau-chih Tseng, Pin-yen Lin, Dmitry Baryshkov
In-Reply-To: <20260327-it61620-0714-v9-0-032938cb9d85@ite.com.tw>

This adds support for the ITE IT61620 bridge chip which converts
MIPI DSI input to HDMI output. The Driver implements the basic
bridge functions and integrates with the DRM bridge and connector
frameworks.

Supported features include:
MIPI DSI input handling
HDMI output setup
Basic mode configuration
I2C-based control and initialization
HDCP 1.4 handling

HPD handling clarification:
Although IT61620 has an HPD pin, hotplug detection is handled by the
system connector. The bridge only receives HPD notifications, and the
HPD pin is used solely for short pulses during HDCP authentication.
Therefore, this bridge does not implement OP_HPD or OP_DETECT, as it
does not originate or determine hotplug or connection status.

This driver will be used on platforms embedding the IT61620 for
video output via HDMI from SoCs with MIPI DSI output.

Add a MAINTAINERS entry for the IT61620 bridge driver.

Signed-off-by: Pet Weng <pet.weng@ite.com.tw>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 MAINTAINERS                          |    7 +
 drivers/gpu/drm/bridge/Kconfig       |   18 +
 drivers/gpu/drm/bridge/Makefile      |    1 +
 drivers/gpu/drm/bridge/ite-it61620.c | 2592 ++++++++++++++++++++++++++++++++++
 4 files changed, 2618 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 0a508f217c964bf1c9002e77d17db250d283c8f6..176aa411645ef44146a41cd7d3cdebcbf428db2c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13654,6 +13654,13 @@ T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
 F:	Documentation/devicetree/bindings/display/bridge/ite,it6263.yaml
 F:	drivers/gpu/drm/bridge/ite-it6263.c
 
+ITE IT61620 MIPI DSI TO HDMI BRIDGE DRIVER
+M:	Pet Weng <pet.weng@ite.com.tw>
+L:	dri-devel@lists.freedesktop.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/display/bridge/ite,it61620.yaml
+F:	drivers/gpu/drm/bridge/ite-it61620.c
+
 ITE IT66121 HDMI BRIDGE DRIVER
 M:	Phong LE <ple@baylibre.com>
 M:	Neil Armstrong <neil.armstrong@linaro.org>
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index c3209b0f46786ec6f9a96c0466b57b0e4a96f44f..7d90010cc63d032b4f740b48d9b3bffe9cd2a9f7 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -118,6 +118,24 @@ config DRM_ITE_IT6263
 	help
 	  ITE IT6263 LVDS to HDMI bridge chip driver.
 
+config DRM_ITE_IT61620
+	tristate "ITE IT61620 DSI/HDMI bridge"
+	depends on OF
+	select DRM_DISPLAY_CONNECTOR
+	select DRM_DISPLAY_HDMI_HELPER
+	select DRM_DISPLAY_HDCP_HELPER
+	select DRM_DISPLAY_HELPER
+	select DRM_MIPI_DSI
+	select DRM_KMS_HELPER
+	select DRM_HDMI_HELPER
+	select CRYPTO_LIB_SHA1
+	help
+	  Driver for ITE IT61620 MIPI DSI to HDMI bridge
+	  chip driver.
+
+	  It enables display output through HDMI when connected to a MIPI
+	  DSI source. The bridge translates the video signals for HDMI monitors.
+
 config DRM_ITE_IT6505
 	tristate "ITE IT6505 DisplayPort bridge"
 	depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index beab5b695a6e1f5a8c39c264567d2b2fff17d6e0..9c50a5420a741f16591633269bd1da06f91befed 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
 
 obj-$(CONFIG_DRM_INNO_HDMI) += inno-hdmi.o
 obj-$(CONFIG_DRM_ITE_IT6263) += ite-it6263.o
+obj-$(CONFIG_DRM_ITE_IT61620) += ite-it61620.o
 obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
 obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
 obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o
diff --git a/drivers/gpu/drm/bridge/ite-it61620.c b/drivers/gpu/drm/bridge/ite-it61620.c
new file mode 100644
index 0000000000000000000000000000000000000000..95771e5a391141a7b4de6a77e4cdd03d75a9dc08
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ite-it61620.c
@@ -0,0 +1,2592 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 ITE Tech. Inc.
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_print.h>
+#include <drm/drm_of.h>
+#include <drm/display/drm_hdcp_helper.h>
+#include <drm/display/drm_hdmi_helper.h>
+#include <drm/display/drm_hdmi_state_helper.h>
+#include <video/videomode.h>
+#include <sound/hdmi-codec.h>
+#include <crypto/sha1.h>
+
+#define EDID_R_BURST_NUM 16
+#define DDC_FIFO_DEPTH 32
+
+#define MIPIRX_I2C_ADDRESS (0x78 >> 1)
+#define TX_I2C_ADDRESS (0xC0 >> 1)
+
+#define REG_CTRL_PW 0xFF
+
+#define REG_VENDER_ID_L 0x00
+#define REG_VENDER_ID_H 0x01
+#define REG_DEVICE_ID_L 0x02
+#define REG_DEVICE_ID_H 0x03
+#define REG_DEV_VERSION 0x04
+#define DEV_VERSION_A0 0xA0
+#define DEV_VERSION_C0 0xC0
+
+#define RX_REG_BANK 0x0F
+#define M_MIPIRX_BANK 0x01
+
+#define RX_REG_RESET_CTRL 0x05
+#define B_REF_CLOCK_RESET BIT(3)
+#define RX_REG_CLK_CTRL 0x10
+#define B_MIPI_INT_STATUS BIT(7)
+#define RX_REG_D_RST 0x1A
+#define B_D_RST BIT(0)
+#define RX_REG_INT_STATUS_01 0x0A
+#define B_INI_V_CHG BIT(0)
+#define RX_REG_INT_POL 0x11
+#define RX_REG_INT_MASK 0x114
+#define B_V_CHG BIT(0)
+#define RX_REG_INT_STATUS_02 0x22
+#define B_INI_V_STB BIT(5)
+#define RX_REG_MPPCLKI 0x12
+
+#define RX_REG_HFP_L 0x30
+#define RX_REG_HFP_H 0x31
+#define RX_REG_HSW_L 0x32
+#define RX_REG_HSW_H 0x33
+#define RX_REG_HBP_L 0x34
+#define RX_REG_HBP_H 0x35
+#define RX_REG_HDEW_L 0x36
+#define RX_REG_HDEW_H 0x37
+#define RX_REG_HTOTAL_L 0x48
+#define RX_REG_HTOTAL_H 0x49
+
+#define RX_REG_SELDCLK 0x24
+#define RX_REG_VFP_L 0x3A
+#define RX_REG_VFP_H 0x3B
+#define RX_REG_VSW_L 0x3C
+#define RX_REG_VSW_H 0x3D
+#define RX_REG_VBP_L 0x3E
+#define RX_REG_VBP_H 0x3F
+#define RX_REG_VDEW_L 0x40
+#define RX_REG_VDEW_H 0x41
+#define RX_REG_POL 0x4E
+#define B_MIPI_H_POL BIT(0)
+#define B_MIPI_V_POL BIT(1)
+
+#define RX_REG_DSCCTRL 0x25
+#define RX_REG_SYNC_NUM 0x4D
+#define RX_REG_BSP_SEL 0x1F
+#define RX_REG_PWD_CTRL 0xE0
+#define RX_REG_ENVRR 0x1AC
+#define RX_REG_ENVBLK 0x1AD
+
+#define RX_REG_D2P_RESET 0xA0
+#define RX_REG_D2P_CTRL 0xA1
+#define RX_REG_AUTO_D2P_RESET 0xA2
+#define RX_REG_TUNEOPT 0xA9
+#define RX_REG_TUNETHRE 0xAA
+#define RX_REG_DSC_RESET 0xAB
+#define RX_REG_DSC_VFRD 0xAC
+
+#define RX_REG_MIPI_CONFIG 0x112
+#define B_MIPI_LANE_NUM 0x03
+#define B_MIPI_PN_SWAP BIT(2)
+#define B_MIPI_LANE_SWAP BIT(3)
+#define RX_REG_HS_CTRL 0x118
+#define RX_REG_LP_CTRL 0x119
+#define RX_REG_MIPI_CTRL01 0x44
+#define RX_REG_AUTO_SYNC 0x144
+#define RX_REG_MV_MAX 0x146
+#define RX_REG_FORCE_M 0x147
+#define B_FORCE_MHT_STB BIT(0)
+#define B_FORCE_MVT_STB BIT(3)
+#define RX_REG_MIPI_CTRL02 0x14E
+#define B_FIFO_RST BIT(5)
+
+#define TX_REG_STATUS01 0x07
+#define B_INT_STATUS BIT(0)
+#define B_HPD_STATUS BIT(1)
+#define B_VIDEO_STB BIT(2)
+
+#define TX_REG_HDMITX_BANK 0x0F
+#define M_HDMITX_BANK 0x03
+#define B_INT_EVENT_HDMI BIT(4)
+#define TX_REG_RESET_1_CTRL 0x05
+#define B_REFERENCE_CLOCK_RESET BIT(0)
+#define B_VIDEO_RESET BIT(1)
+#define B_AUDIO_RESET BIT(2)
+#define B_AUX_RESET BIT(3)
+#define B_IPCLK_RESET BIT(4)
+#define B_SDM_RESET BIT(5)
+#define B_TCLK_RESET BIT(6)
+
+#define TX_REG_RESET_2_CTRL 0x06
+
+#define TX_REG_V_STS 0x09
+#define B_RXSEN BIT(1)
+#define B_V_STABLE BIT(2)
+#define B_TMDS_STABLE BIT(3)
+
+#define TX_REG_SYS_CONFIG 0x0C
+#define B_EN_HDMI BIT(6)
+#define B_INT_POL BIT(0)
+#define B_INT_MODE BIT(1)
+#define B_INT_OUT_STATUS BIT(2)
+#define B_INT_OUTPUT BIT(3)
+
+#define BIT_OFFSET(x) ((x) * BITS_PER_BYTE)
+
+#define TX_REG_INT1 0x10
+#define TX_REG_INT_MASK1 0x18
+#define INT_HPD_CHG 0
+#define B_HPD_CHG BIT(INT_HPD_CHG)
+#define BIT_HPD_CHG (BIT_OFFSET(0) + INT_HPD_CHG)
+#define INT_RXSEN_CHANGE 2
+#define B_RXSEN_CHANGE BIT(INT_RXSEN_CHANGE)
+#define BIT_RXSEN_CHANGE (BIT_OFFSET(0) + INT_RXSEN_CHANGE)
+#define INT_AUTH_F 3
+#define B_INT_AUTH_F BIT(INT_AUTH_F)
+#define BIT_INT_AUTH_F (BIT_OFFSET(0) + INT_AUTH_F)
+#define INT_AUTH_D 4
+#define B_INT_AUTH_D BIT(INT_AUTH_D)
+#define BIT_INT_AUTH_D (BIT_OFFSET(0) + INT_AUTH_D)
+
+#define TX_REG_INT2 0x11
+#define TX_REG_INT_MASK2 0x19
+#define INT_KSV_CHECK 1
+#define B_KSV_CHECK BIT(INT_KSV_CHECK)
+#define BIT_KSV_CHECK  (BIT_OFFSET(1) + INT_KSV_CHECK)
+
+#define TX_REG_INT3 0x16
+#define TX_REG_INT_MASK3 0x1E
+#define INT_TMDS_STB_CHG 6
+#define B_TMDS_STB_CHG BIT(INT_TMDS_STB_CHG)
+#define BIT_TMDS_STB_CHG (BIT_OFFSET(2) + INT_TMDS_STB_CHG)
+
+#define REG_TX_INT_CTRL 0x21
+#define B_DIS_INT_OUTPUT BIT(5)
+#define TX_REG_CLOCK_PWD_CTRL 0x2B
+
+#define TX_REG_AFE0E 0x0E
+#define TX_REG_AFE30 0x30
+#define TX_REG_AFE33 0x33
+#define TX_REG_AFE34 0x34
+#define TX_REG_AFE35 0x35
+#define TX_REG_AFEE9 0xE9
+#define TX_REG_AFE_XP 0x219
+#define TX_REG_AFE_XLC1 0x220
+#define TX_REG_AFE_XLC2 0x223
+#define TX_REG_AFE_XLC3 0x226
+#define TX_REG_AFE_DRV 0x23B
+
+#define TX_REG_CR_1_CTRL 0x3A
+#define TX_REG_CR_2_CTRL 0x3F
+
+#define TX_REG_R0TM 0x5A
+#define TX_REG_SHA_SEL 0x5D
+
+#define TX_REG_HDCP_CTRL1 0x60
+#define B_CPDESIRED BIT(0)
+#define TX_REG_HDCP_PRO 0x61
+#define B_AUTH_FIRE BIT(0)
+#define B_LIST_CHK_DONE BIT(4)
+#define B_LIST_CHK_FAIL BIT(5)
+#define TX_REG_HDCP_CTRL2 0x62
+#define TX_REG_AUTOMUTE 0x65
+#define B_AN_SEL BIT(0)
+#define B_EN_AN_GEN BIT(1)
+#define B_EN_M0_RD BIT(5)
+#define TX_REG_HDCP_AUTH_CS 0x66
+#define M_AUTH_CH 0x7F
+#define CS_AUTH_DONE 0x4E
+#define CS_AUTH_FAIL 0x02
+#define CS_KSVLIST_CHK 0x19
+#define TX_REG_HDCP_CTRL3 0x1BA
+#define B_ENC_DIS BIT(6)
+#define B_PAUSE BIT(7)
+
+#define TX_REG_VD_CTRL1 0xA8
+#define TX_REG_VD_CTRL2 0xA4
+#define B_VIDEO_FIFO_REST BIT(0)
+#define TX_REG_VD_CTRL3 0xB7
+
+#define TX_REG_AUD_SPDIF 0x23
+#define TX_REG_AUD_CTRL 0xB8
+#define TX_REG_AUD_FMT 0xBA
+#define TX_REG_EN_AUDIO 0xBB
+#define TX_REG_AUD_FIFO1 0xBC
+#define TX_REG_AUD_FIFO2 0xBD
+#define TX_REG_AUD_CTS 0x1BC
+#define TX_REG_AUD_STS1 0x1F0
+#define B_EN_AUD_NLPCM BIT(1)
+#define TX_REG_AUD_STS2 0x1F3
+#define TX_REG_AUD_STS3 0x1F4
+
+#define TX_REG_LINK_CTRL0 0xD3
+#define B_EN_AUDIO_MUTE BIT(5)
+
+#define TX_REG_V_QUEUE 0xE7
+#define TX_REG_V_TU 0xEB
+
+#define TX_REG_HPD_CONFIG 0xF6
+
+#define TX_REG_CEC_CONFIG 0xFA
+#define B_EN_CEC BIT(0)
+
+#define TX_REG_DDC_CTRL1 0x1A0
+#define TX_REG_DDC_ADDR 0x1A1
+#define DDC_HDCP_ADDR (DRM_HDCP_DDC_ADDR << 1)
+#define DDC_EDID_ADDR (DDC_ADDR << 1)
+#define TX_REG_DDC_OFFSET 0x1A2
+#define TX_REG_DDC_NUM_L 0x1A3
+#define TX_REG_DDC_NUM_H 0x1A4
+#define TX_REG_DDC_SEGMENT 0x1A5
+#define TX_REG_DDC_COMMAND 0x1A6
+#define DDC_COMMAND_BURST_R 0X00
+#define DDC_COMMAND_BURST_W 0X01
+#define DDC_COMMAND_EDID_RD 0X03
+#define DDC_COMMAND_FIFO_CLR 0X09
+#define DDC_COMMAND_ABORT 0X0F
+#define TX_REG_DDC_CTRL2 0x1AD
+#define B_DDC_REST BIT(4)
+#define TX_REG_DDC_STATUS 0x1A7
+#define B_DDC_TX_DONE BIT(7)
+#define B_DDC_NOACK BIT(5)
+#define B_DDC_FULL BIT(2)
+#define TX_REG_DDC_FIFO 0x1A8
+#define TX_REG_DDC_FIFO_STS 0x1AE
+#define M_DDC_STAGE_NUM	0x3F
+
+#define TX_REG_HDMI_CTRL1 0x1B8
+#define B_EN_HDMI_MODE BIT(0)
+#define TX_REG_HDMI_CTRL2 0x1B9
+#define B_EN_AVMUTE BIT(0)
+#define TX_REG_EN_PKT1 0x1BF
+#define B_EN_AVI BIT(0)
+#define B_AVI_RP BIT(1)
+#define B_EN_AUD BIT(2)
+#define B_EN_AUD_RP BIT(3)
+#define B_EN_VSIF BIT(6)
+#define B_EN_VSIF_RP BIT(7)
+#define TX_REG_EN_PKT2 0x1C0
+#define B_EN_NULL BIT(0)
+#define B_EN_NULL_RP BIT(1)
+#define B_EN_GEN BIT(4)
+#define B_GEN_RP BIT(5)
+
+#define TX_REG_VH_TIME 0x165
+#define TX_REG_PG_HFP_L 0x150
+#define TX_REG_PG_HFP_H 0x151
+#define TX_REG_PG_HSW_L 0x152
+#define TX_REG_PG_HSW_H 0x153
+#define TX_REG_PG_HBP_L 0x154
+#define TX_REG_PG_HBP_H 0x155
+#define TX_REG_PG_DEW_L 0x156
+#define TX_REG_PG_DEW_H 0x157
+#define TX_REG_PG_HVR2_L 0x158
+#define TX_REG_PG_HVR2_H 0x159
+#define TX_REG_PG_VFP_L 0x15A
+#define TX_REG_PG_VFP_H 0x15B
+#define TX_REG_PG_VSW_L 0x15C
+#define TX_REG_PG_VSW_H 0x15D
+#define TX_REG_PG_VBP_L 0x15E
+#define TX_REG_PG_VBP_H 0x15F
+#define TX_REG_PG_VDEW_L 0x160
+#define TX_REG_PG_HDEW_H 0x161
+#define TX_REG_PG_VFP2_L 0x162
+#define TX_REG_PG_VFP2_H 0x163
+#define TX_REG_PG_POL 0x164
+
+#define TX_REG_AVIINFO_DB00 0x1D0
+#define TX_REG_AVIINFO_DB01 0x1D1
+#define TX_REG_AVIINFO_DB02 0x1D2
+#define TX_REG_AVIINFO_DB03 0x1D3
+#define TX_REG_AVIINFO_DB04 0x1D4
+#define TX_REG_AVIINFO_DB05 0x1D5
+#define TX_REG_AVIINFO_DB06 0x1D6
+#define TX_REG_AVIINFO_DB07 0x1D7
+#define TX_REG_AVIINFO_DB08 0x1D8
+#define TX_REG_AVIINFO_DB09 0x1D9
+#define TX_REG_AVIINFO_DB10 0x1DA
+#define TX_REG_AVIINFO_DB11 0x1DB
+#define TX_REG_AVIINFO_DB12 0x1DC
+#define TX_REG_AVIINFO_DB13 0x1DD
+#define TX_REG_AVIINFO_DB14 0x1DE
+
+#define TX_REG_AUDINFO_DB01 0x1E0
+#define TX_REG_AUDINFO_DB02 0x1E1
+#define TX_REG_AUDINFO_DB03 0x1E2
+#define TX_REG_AUDINFO_DB04 0x1E3
+#define TX_REG_AUDINFO_DB05 0x1E4
+#define TX_REG_AUDINFO_DB06 0x1E5
+#define TX_REG_AUDINFO_DB07 0x1E6
+#define TX_REG_AUDINFO_DB08 0x1E7
+#define TX_REG_AUDINFO_DB09 0x1E8
+#define TX_REG_AUDINFO_DB10 0x1E9
+
+#define TX_REG_NULLPKT_HB00 0x310
+#define TX_REG_NULLPKT_HB01 0x311
+#define TX_REG_NULLPKT_HB02 0x312
+#define TX_REG_NULLPKT_PB00 0x314
+#define TX_REG_NULLPKT_PB27 0x32F
+
+#define TX_REG_VSIFPKT_HB02 0x37E
+#define TX_REG_VSIFPKT_PB00 0x380
+#define TX_REG_VSIFPKT_PB01 0x381
+
+#define TX_REG_SSC_PD 0x211
+
+#define TX_REG_TXPLL_CTRL 0x218
+#define TX_REG_XLC_7_CTRL 0x227
+
+#define TX_REG_TXDRV_PD_CTRL 0x23D
+#define TX_REG_TXDRV_CTRL 0x240
+#define TX_REG_AUX_CTRL 0x244
+#define TX_REG_IPLL_CTRL 0x248
+#define TX_REG_RDROM_TRG 0x271
+
+#define TX_REG_HDCP_ARI_L 0x63
+#define TX_REG_HDCP_ARI_H 0x64
+
+#define TX_REG_HDCP_AUTH_STS 0x67
+
+#define TX_REG_HDCP_BKSV1 0x68
+#define TX_REG_HDCP_BKSV2 0x69
+#define TX_REG_HDCP_BKSV3 0x6A
+#define TX_REG_HDCP_BKSV4 0x6B
+#define TX_REG_HDCP_BKSV5 0x6C
+
+#define TX_REG_HDCP_BRI_L 0x6D
+#define TX_REG_HDCP_BRI_H 0x6E
+
+#define TX_REG_HDCP_AKSV1 0x6F
+#define TX_REG_HDCP_AKSV2 0x70
+#define TX_REG_HDCP_AKSV3 0x71
+#define TX_REG_HDCP_AKSV4 0x72
+#define TX_REG_HDCP_AKSV5 0x73
+
+#define TX_REG_ANM0V1 0x74
+#define TX_REG_ANM0V2 0x75
+#define TX_REG_ANM0V3 0x76
+#define TX_REG_HDCP_AN_M0_V4 0x77
+#define TX_REG_HDCP_AN_M05 0x78
+#define TX_REG_HDCP_AN_M06 0x79
+#define TX_REG_HDCP_AN_M07 0x7A
+#define TX_REG_HDCP_AN_M08 0x7B
+
+#define TX_REG_HDCP_BCAPS 0x7C
+#define B_KSV_READY BIT(5)
+#define TX_REG_HDCP_BSTS_L 0x7D
+#define TX_REG_HDCP_BSTS_H 0x7E
+
+#define TX_REG_AUDPKT_CTS_0 0x3A8
+#define TX_REG_AUDPKT_CTS_1 0x3A9
+#define TX_REG_AUDPKT_CTS_2 0x3AA
+
+#define TX_REG_AUDPKT_N_0 0x3AB
+#define TX_REG_AUDPKT_N_1 0x3AC
+#define TX_REG_AUDPKT_N_2 0x3AD
+
+#define SOFT_DDC_TIMEOUT_MS 100
+#define HDCP_KSV_LIST_TIMEOUT_MS 5000
+
+#define MAX_HDCP_DOWN_STREAM_COUNT 127
+#define HDCP_SHA1_FIFO_LEN (MAX_HDCP_DOWN_STREAM_COUNT * DRM_HDCP_KSV_LEN + 10)
+
+#define HI_BYTE(x) (((x) >> 8) & 0xFF)
+#define LO_BYTE(x) ((x) & 0xFF)
+
+enum video_state {
+	it61620_VIDEO_OFF = 0x00,
+	it61620_VIDEO_WAIT,
+	it61620_VIDEO_ON,
+};
+
+enum hdcp_state {
+	CP_NONE = 0x00,
+	CP_GOING = 0x01,
+};
+
+enum it61620_audio_select {
+	I2S = 0,
+	SPDIF,
+};
+
+enum it61620_audio_word_length {
+	WORD_LENGTH_16BIT = 0x0,
+	WORD_LENGTH_20BIT = 0x2,
+	WORD_LENGTH_24BIT = 0x3,
+};
+
+enum it61620_audio_sample_rate {
+	SAMPLE_RATE_32K = 0x3,
+	SAMPLE_RATE_48K = 0x2,
+	SAMPLE_RATE_64K = 0xb,
+	SAMPLE_RATE_96K = 0xa,
+	SAMPLE_RATE_192K = 0xe,
+	SAMPLE_RATE_44_1K = 0x0,
+	SAMPLE_RATE_88_2K = 0x8,
+	SAMPLE_RATE_176_4K = 0xc,
+};
+
+enum it61620_audio_type {
+	LPCM = 0,
+	NLPCM,
+};
+
+enum it61620_audio_i2s_input_format {
+	I2S_INPUT_FORMAT_STANDARD = 0,
+	I2S_INPUT_FORMAT_32BIT = 1,
+};
+
+struct it6162_chip_info {
+	u16 vid;
+	u16 pid;
+};
+
+struct it61620_mipirx {
+	u8 lane_num;
+	u8 pn_swap;
+	u8 lane_swap;
+};
+
+struct it61620_hdmi_afe_setting {
+	unsigned int clock;
+	unsigned int h2on_pll;
+	unsigned int hs;
+	unsigned int afe_val[24];
+};
+
+struct it61620 {
+	struct drm_bridge bridge;
+	struct drm_connector *connector;
+	struct device *dev;
+	enum drm_connector_status connector_status;
+	struct drm_device *drm;
+	struct drm_bridge *next_bridge;
+
+	struct i2c_client *it61620_i2c;
+	struct i2c_client *mipirx_i2c;
+	struct i2c_client *tx_i2c;
+	struct regmap *it61620_regmap;
+	struct regmap *mipirx_regmap;
+	struct regmap *tx_regmap;
+
+	struct delayed_work hdcp_work;
+	struct wait_queue_head wq;
+
+	struct regulator *ovdd1833;
+	struct regulator *ivdd;
+	struct regulator *ovdd33;
+	struct gpio_desc *gpiod_reset;
+
+	bool powered;
+	bool is_hdmi;
+	bool en_audio;
+	bool hpd;
+	u8 dev_ver;
+
+	/* operations can only be served one at the time */
+	struct mutex ddc_lock;
+
+	enum video_state video_state;
+	enum it61620_audio_i2s_input_format i2s_input_format;
+
+	/* it61620 DSI RX related params */
+	struct mipi_dsi_device *dsi;
+
+	struct it61620_mipirx mipirx_config;
+
+	unsigned long tmds_char_rate;
+
+	enum hdcp_state hdcp_state;
+	int hdcp_cp;
+	u8 sha1_input[HDCP_SHA1_FIFO_LEN];
+	const struct it6162_chip_info *chip_info;
+};
+
+static inline struct it61620 *bridge_to_it61620(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct it61620, bridge);
+}
+
+static const struct regmap_config it61620_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0xff,
+	.cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_range it61620_tx_volatile_ranges[] = {
+	{ .range_min = 0, .range_max = 0x3ff},
+};
+
+static const struct regmap_access_table it61620_tx_volatile_table = {
+	.yes_ranges = it61620_tx_volatile_ranges,
+	.n_yes_ranges = ARRAY_SIZE(it61620_tx_volatile_ranges),
+};
+
+static const struct regmap_range_cfg it61620_tx_regmap_ranges[] = {
+	{
+		.range_min = 0,
+		.range_max = 0x3ff,
+		.selector_reg = TX_REG_HDMITX_BANK,
+		.selector_mask = M_HDMITX_BANK,
+		.selector_shift = 0,
+		.window_start = 0x00,
+		.window_len = 0x100,
+	},
+};
+
+static const struct regmap_config it61620_tx_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.volatile_table = &it61620_tx_volatile_table,
+	.ranges = it61620_tx_regmap_ranges,
+	.num_ranges = ARRAY_SIZE(it61620_tx_regmap_ranges),
+	.max_register = 0x3ff,
+	.cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_range it61620_mipirx_volatile_ranges[] = {
+	{ .range_min = 0, .range_max = 0x1ff },
+};
+
+static const struct regmap_access_table it61620_mipirx_volatile_table = {
+	.yes_ranges = it61620_mipirx_volatile_ranges,
+	.n_yes_ranges = ARRAY_SIZE(it61620_mipirx_volatile_ranges),
+};
+
+static const struct regmap_range_cfg it61620_mipirx_regmap_ranges[] = {
+	{
+		.range_min = 0,
+		.range_max = 0x1ff,
+		.selector_reg = RX_REG_BANK,
+		.selector_mask = M_MIPIRX_BANK,
+		.selector_shift = 0,
+		.window_start = 0x00,
+		.window_len = 0x100,
+	},
+};
+
+static const struct regmap_config it61620_mipi_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.volatile_table = &it61620_mipirx_volatile_table,
+	.ranges = it61620_mipirx_regmap_ranges,
+	.num_ranges = ARRAY_SIZE(it61620_mipirx_regmap_ranges),
+	.max_register = 0x1ff,
+	.cache_type = REGCACHE_NONE,
+};
+
+static void it61620_mipi_reset_video(struct it61620 *it61620)
+{
+	regmap_write(it61620->mipirx_regmap, RX_REG_DSC_RESET, 0x38);
+	usleep_range(100, 200);
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_D2P_RESET, 0x70, 0x70);
+	regmap_write(it61620->mipirx_regmap, RX_REG_DSC_RESET, 0x00);
+
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_RESET_CTRL, 0x57, 0x00);
+	usleep_range(1000, 2000);
+}
+
+static void it61620_mipi_m2p_reset(struct it61620 *it61620)
+{
+	if (it61620->dev_ver != DEV_VERSION_A0)
+		return;
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_MIPI_CTRL02, B_FIFO_RST,
+			   B_FIFO_RST);
+	usleep_range(1000, 2000);
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_MIPI_CTRL02, B_FIFO_RST,
+			   0x00);
+}
+
+static void it61620_mipi_d2p_reset_fifo(struct it61620 *it61620)
+{
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_D2P_RESET, 0x70, 0x70);
+	usleep_range(1000, 2000);
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_D2P_RESET, 0x70, 0x50);
+	usleep_range(100, 200);
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_D2P_RESET, 0x70, 0x10);
+	usleep_range(100, 200);
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_D2P_RESET, 0x70, 0x00);
+}
+
+static void it61620_mipi_d2p_reset(struct it61620 *it61620)
+{
+	it61620_mipi_m2p_reset(it61620);
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_D_RST, B_D_RST, B_D_RST);
+	usleep_range(1000, 2000);
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_D_RST, B_D_RST, 0x00);
+	it61620_mipi_d2p_reset_fifo(it61620);
+}
+
+static void it61620_mipi_reset(struct it61620 *it61620)
+{
+	unsigned int val;
+	u8 dev_ver = it61620->dev_ver;
+	struct it61620_mipirx *mipirx = &it61620->mipirx_config;
+
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_RESET_CTRL, B_REF_CLOCK_RESET,
+			   B_REF_CLOCK_RESET);
+	usleep_range(1000, 2000);
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_RESET_CTRL, B_REF_CLOCK_RESET,
+			   0x00);
+	usleep_range(1000, 2000);
+
+	it61620_mipi_d2p_reset(it61620);
+
+	regmap_write(it61620->mipirx_regmap, RX_REG_HS_CTRL, 0x93);
+	regmap_write(it61620->mipirx_regmap, RX_REG_LP_CTRL, 0x0c);
+	regmap_write(it61620->mipirx_regmap, RX_REG_AUTO_SYNC, 0x01);
+	regmap_write(it61620->mipirx_regmap, RX_REG_MV_MAX, 0x0f);
+	regmap_write(it61620->mipirx_regmap, RX_REG_FORCE_M, B_FORCE_MHT_STB);
+
+	regmap_write(it61620->mipirx_regmap, RX_REG_MIPI_CTRL01, 0x2c);
+	regmap_write(it61620->mipirx_regmap, RX_REG_AUTO_D2P_RESET, 0x13);
+	regmap_write(it61620->mipirx_regmap, RX_REG_D2P_CTRL, 0x28);
+	regmap_write(it61620->mipirx_regmap, RX_REG_TUNEOPT, 0x9c);
+
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_CLK_CTRL, 0x30, 0x20);
+
+	if (dev_ver != DEV_VERSION_A0) {
+		regmap_write(it61620->mipirx_regmap, RX_REG_TUNETHRE, 0x02);
+		regmap_write(it61620->mipirx_regmap, RX_REG_AUTO_D2P_RESET, 0x83);
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_FORCE_M, B_FORCE_MVT_STB,
+				   B_FORCE_MVT_STB);
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_SYNC_NUM, 0xf7, 0x44);
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_BSP_SEL, 0x03, 0x02);
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_PWD_CTRL, 0x24, 0x24);
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_ENVRR, 0x01, 0x00);
+		regmap_write(it61620->mipirx_regmap, RX_REG_ENVBLK, 0x00);
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_DSCCTRL, 0x40, 0x00);
+	}
+	val = (mipirx->lane_num - 1) |
+	      (mipirx->pn_swap ? B_MIPI_PN_SWAP : 0x00) |
+	      (mipirx->lane_swap ? B_MIPI_LANE_SWAP : 0x00);
+
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_MIPI_CONFIG, 0x0f, val);
+
+	it61620_mipi_reset_video(it61620);
+}
+
+static void it61620_mipi_video_change_irq(struct it61620 *it61620)
+{
+	unsigned int reg22;
+	unsigned int video_stable;
+
+	regmap_read(it61620->mipirx_regmap, RX_REG_INT_STATUS_02, &reg22);
+	video_stable = reg22 & B_INI_V_STB;
+
+	if (video_stable) {
+		it61620_mipi_m2p_reset(it61620);
+		it61620_mipi_d2p_reset_fifo(it61620);
+	} else {
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_D_RST, 0x01, 0x01);
+		usleep_range(1000, 2000);
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_D_RST, 0x01, 0x00);
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_D2P_RESET, 0x70, 0x70);
+	}
+}
+
+static void it61620_mipi_irq_handler(struct it61620 *it61620)
+{
+	unsigned int reg0a, reg_val;
+
+	regmap_read(it61620->mipirx_regmap, RX_REG_CLK_CTRL, &reg_val);
+	reg_val &= B_MIPI_INT_STATUS;
+	if (reg_val == 0)
+		return;
+
+	regmap_read(it61620->mipirx_regmap, RX_REG_INT_STATUS_01, &reg0a);
+	regmap_write(it61620->mipirx_regmap, RX_REG_INT_STATUS_01, reg0a);
+	if (reg0a & B_INI_V_CHG)
+		it61620_mipi_video_change_irq(it61620);
+}
+
+static void it61620_show_drm_video_mode(struct it61620 *it61620,
+					const struct drm_display_mode *mode)
+{
+	struct drm_device *drm = it61620->drm;
+
+	drm_dbg_kms(drm, "HActive = %u\n", mode->hdisplay);
+	drm_dbg_kms(drm, "VActive = %u\n", mode->vdisplay);
+	drm_dbg_kms(drm, "HTotal =  %u\n", mode->htotal);
+	drm_dbg_kms(drm, "VTotal =  %u\n", mode->vtotal);
+	drm_dbg_kms(drm, "PCLK = %u khz\n", mode->clock);
+	drm_dbg_kms(drm, "HFP = %u\n", mode->hsync_start - mode->hdisplay);
+	drm_dbg_kms(drm, "HSW = %u\n", mode->hsync_end - mode->hsync_start);
+	drm_dbg_kms(drm, "HBP = %u\n", mode->htotal - mode->hsync_end);
+	drm_dbg_kms(drm, "VFP = %u\n", mode->vsync_start - mode->vdisplay);
+	drm_dbg_kms(drm, "VSW = %u\n", mode->vsync_end - mode->vsync_start);
+	drm_dbg_kms(drm, "VBP = %u\n", mode->vtotal - mode->vsync_end);
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		drm_dbg_kms(drm, "HPOL +\n");
+	else
+		drm_dbg_kms(drm, "HPOL -\n");
+
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		drm_dbg_kms(drm, "VPOL +\n");
+	else
+		drm_dbg_kms(drm, "VPOL -\n");
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		drm_dbg_kms(drm, "Intelaced\n");
+	else
+		drm_dbg_kms(drm, "Progressive\n");
+}
+
+static inline void it61620_write16(struct regmap *map,
+				   unsigned int reg, u16 val)
+{
+	u8 data[2] = {val & 0xff, val >> 8};
+
+	regmap_bulk_write(map, reg, data, 2);
+}
+
+static void it61620_mipi_set_d2v_video_timing(struct it61620 *it61620,
+					      struct drm_display_mode *mode)
+{
+	u8 d2vffrd_adr_dly;
+	u32 htotal, hfp, hsw, hbp, hdew;
+	u32 vfp, vsw, vbp, vdew;
+	u32 clock;
+	bool hpol_high = 0, vpol_high = 0;
+
+	it61620_show_drm_video_mode(it61620, mode);
+
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		hpol_high = true;
+
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		vpol_high = true;
+
+	clock = mode->clock;
+	hdew = mode->hdisplay;
+
+	hfp = mode->hsync_start - mode->hdisplay;
+	hsw = mode->hsync_end - mode->hsync_start;
+	hbp = mode->htotal - mode->hsync_end;
+	htotal = mode->htotal;
+
+	vdew = mode->vdisplay;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vsw = mode->vsync_end - mode->vsync_start;
+	vbp = mode->vtotal - mode->vsync_end;
+
+	if (it61620->dev_ver != DEV_VERSION_A0)
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_SELDCLK, 0x1c, 0x04);
+
+	if (hdew > 1920) {
+		if (htotal < 4272) {
+			d2vffrd_adr_dly = abs(htotal - 2880) / 24;
+			regmap_write(it61620->mipirx_regmap, RX_REG_DSC_VFRD,
+				     d2vffrd_adr_dly);
+		} else {
+			regmap_write(it61620->mipirx_regmap, RX_REG_DSC_VFRD, 0x50);
+		}
+	} else {
+		regmap_write(it61620->mipirx_regmap, RX_REG_DSC_VFRD, hdew / 36);
+	}
+
+	it61620_write16(it61620->mipirx_regmap, RX_REG_HFP_L, hfp);
+	it61620_write16(it61620->mipirx_regmap, RX_REG_HSW_L, hsw);
+	it61620_write16(it61620->mipirx_regmap, RX_REG_HBP_L, hbp);
+	it61620_write16(it61620->mipirx_regmap, RX_REG_HDEW_L, hdew);
+	it61620_write16(it61620->mipirx_regmap, RX_REG_HTOTAL_L, htotal);
+	it61620_write16(it61620->mipirx_regmap, RX_REG_VFP_L, vfp);
+	it61620_write16(it61620->mipirx_regmap, RX_REG_VSW_L, vsw);
+	it61620_write16(it61620->mipirx_regmap, RX_REG_VBP_L, vbp);
+	it61620_write16(it61620->mipirx_regmap, RX_REG_VDEW_L, vdew);
+
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_POL, 0x03,
+			   ((!vpol_high) << 1) | (!hpol_high));
+
+	if (it61620->dev_ver == DEV_VERSION_A0 &&
+	    ((hdew == 2560 && vdew == 1440 && clock == 241500) ||
+	     (hdew == 720 && vdew == 400 && clock == 28320) ||
+	     (hdew == 720 && vdew == 576 && clock == 27000))) {
+		dev_dbg(it61620->dev, "device A0 %dx%d %d Khz",
+			hdew, vdew, clock);
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_MPPCLKI, 0x80, 0x80);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_VH_TIME, 0x48, 0x48);
+
+		it61620_write16(it61620->mipirx_regmap, TX_REG_PG_HFP_L, hfp);
+		it61620_write16(it61620->mipirx_regmap, TX_REG_PG_HSW_L, hsw);
+		it61620_write16(it61620->mipirx_regmap, TX_REG_PG_HBP_L, hbp);
+		it61620_write16(it61620->mipirx_regmap, TX_REG_PG_DEW_L, hdew);
+		it61620_write16(it61620->mipirx_regmap, TX_REG_PG_HVR2_L, 0x0fff);
+		it61620_write16(it61620->mipirx_regmap, TX_REG_PG_VFP_L, vfp);
+		it61620_write16(it61620->mipirx_regmap, TX_REG_PG_VSW_L, vsw);
+		it61620_write16(it61620->mipirx_regmap, TX_REG_PG_VBP_L, vbp);
+		it61620_write16(it61620->mipirx_regmap, TX_REG_PG_VDEW_L, vdew);
+		it61620_write16(it61620->mipirx_regmap, TX_REG_PG_VFP2_L, 0x0fff);
+
+		regmap_update_bits(it61620->tx_regmap, TX_REG_PG_POL, 0x07,
+				   ((vpol_high) << 1) | (hpol_high));
+
+	} else {
+		regmap_update_bits(it61620->mipirx_regmap, RX_REG_MPPCLKI, 0x80, 0x00);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_VH_TIME, 0x48, 0x00);
+	}
+
+	it61620_mipi_d2p_reset(it61620);
+}
+
+static void it61620_hdmi_reset(struct it61620 *it61620)
+{
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_1_CTRL, 0x7e, 0x7e);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_2_CTRL, 0x36, 0x36);
+	usleep_range(1000, 2000);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_1_CTRL, 0x1f, 0x00);
+	usleep_range(1000, 2000);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_1_CTRL, 0x40, 0x00);
+
+	regmap_write(it61620->tx_regmap, TX_REG_RDROM_TRG, 0x04);
+	usleep_range(1000, 2000);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_SYS_CONFIG, B_EN_HDMI, B_EN_HDMI);
+
+	/* Reset AFE */
+	regmap_update_bits(it61620->tx_regmap, TX_REG_XLC_7_CTRL, 0x04, 0x00);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_IPLL_CTRL, 0x02, 0x00);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXPLL_CTRL, 0x04, 0x00);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXDRV_CTRL, 0x01, 0x00);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_XLC_7_CTRL, 0x04, 0x04);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_IPLL_CTRL, 0x02, 0x02);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXPLL_CTRL, 0x04, 0x04);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXDRV_CTRL, 0x01, 0x01);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXDRV_CTRL, 0x08, 0x08);
+
+	regmap_write(it61620->tx_regmap, TX_REG_V_QUEUE, 0x08);
+	regmap_write(it61620->tx_regmap, TX_REG_V_TU, 0x07);
+
+	regmap_write(it61620->tx_regmap, TX_REG_CR_1_CTRL, 0x09);
+
+	regmap_write(it61620->tx_regmap, TX_REG_CR_2_CTRL, 0x16);
+
+	regmap_write(it61620->tx_regmap, TX_REG_VD_CTRL1, 0x04);
+
+	regmap_write(it61620->tx_regmap, TX_REG_HDCP_CTRL2, 0x04);
+
+	regmap_write(it61620->tx_regmap, TX_REG_VD_CTRL3, 0x84);
+	regmap_write(it61620->tx_regmap, TX_REG_HPD_CONFIG, 0x06);
+	usleep_range(1000, 2000);
+
+	regmap_write(it61620->tx_regmap, TX_REG_VD_CTRL2, 0x2e);
+	regmap_write(it61620->tx_regmap, TX_REG_HDMI_CTRL1, 0x30);
+
+	regmap_write(it61620->tx_regmap, TX_REG_HDMI_CTRL2, 0x04);
+}
+
+static void it61620_hdmi_poweron(struct it61620 *it61620)
+{
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXDRV_PD_CTRL, 0x70, 0x00);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_XLC_7_CTRL, 0x07, 0x06);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_IPLL_CTRL, 0x12, 0x12);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXPLL_CTRL, 0xc6, 0x84);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_SSC_PD, 0x03, 0x03);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_CLOCK_PWD_CTRL, 0x0f, 0x00);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_VD_CTRL2, B_VIDEO_FIFO_REST,
+			   B_VIDEO_FIFO_REST);
+	usleep_range(1000, 2000);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_VD_CTRL2, B_VIDEO_FIFO_REST,
+			   0x00);
+}
+
+static void it61620_hdmi_powerdown(struct it61620 *it61620)
+{
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXDRV_PD_CTRL, 0x70, 0x70);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_SSC_PD, 0x03, 0x00);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_IPLL_CTRL, 0x12, 0x00);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXPLL_CTRL, 0xc6, 0xc2);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_XLC_7_CTRL, 0x07, 0x01);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXDRV_CTRL, 0xf9, 0x00);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_AUX_CTRL, 0x90, 0x00);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_CLOCK_PWD_CTRL, 0x0f, 0x0a);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_CEC_CONFIG, B_EN_CEC, 0x00);
+}
+
+static bool it61620_hdmi_get_hpd_status(struct it61620 *it61620)
+{
+	unsigned int val;
+
+	regmap_read(it61620->tx_regmap, TX_REG_STATUS01, &val);
+	return !!(val & B_HPD_STATUS);
+}
+
+static void it61620_hdmi_ddc_abort(struct it61620 *it61620)
+{
+	struct drm_device *drm = it61620->drm;
+	unsigned int val;
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_DDC_CTRL2, B_DDC_REST, B_DDC_REST);
+	usleep_range(1000, 2000);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_DDC_CTRL2, B_DDC_REST, 0x00);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_COMMAND, DDC_COMMAND_ABORT);
+	usleep_range(1000, 2000);
+	regmap_read(it61620->tx_regmap, TX_REG_DDC_STATUS, &val);
+	drm_dbg(drm, "tx ddc ststus %02X\n", val);
+}
+
+static unsigned int it61620_hdmi_read_ddc_status(struct it61620 *it61620)
+{
+	unsigned int val;
+
+	regmap_read(it61620->tx_regmap, TX_REG_DDC_STATUS, &val);
+	return val;
+}
+
+static int it61620_hdmi_ddc_wait(struct it61620 *it61620)
+{
+	struct drm_device *drm = it61620->drm;
+	unsigned int ddc_status;
+	int ret = 0;
+
+	ret = readx_poll_timeout(it61620_hdmi_read_ddc_status,
+				 it61620, ddc_status,
+				 (ddc_status &
+				 (B_DDC_NOACK | B_DDC_TX_DONE | B_DDC_FULL)),
+				 2000,
+				 1000 * SOFT_DDC_TIMEOUT_MS);
+
+	if (ret < 0) {
+		drm_dbg(drm, "DDC SOFT timeout %x\n", ddc_status);
+	} else {
+		if (ddc_status & B_DDC_NOACK) {
+			drm_dbg(drm, "DDC no ack");
+			ret = -EIO;
+		}
+
+		if (ddc_status & B_DDC_FULL)
+			drm_dbg(drm, "DDC FULL");
+	}
+	return ret;
+}
+
+static int it61620_hdmi_get_ddc_fifo(struct it61620 *it61620, u8 *buf,
+				     size_t len)
+{
+	int err;
+	struct device *dev = it61620->dev;
+
+	if (!it61620->powered)
+		return -ENODEV;
+
+	err = regmap_bulk_read(it61620->tx_regmap, TX_REG_DDC_FIFO, buf, len);
+	if (err < 0) {
+		dev_err(dev, "read ddc fifo failed tx reg[0x%x] err = %d",
+			TX_REG_DDC_FIFO, err);
+		return err;
+	}
+
+	return 0;
+}
+
+static const struct it61620_hdmi_afe_setting hdmi_afe[4] = {
+	{375000, 0x01, 0x02,
+		{0x03, 0x53, 0x1A, 0x03, 0x00, 0x04,
+		0x03, 0x53, 0x1A, 0x03, 0x00, 0x04,
+		0x03, 0x53, 0x1A, 0x03, 0x00, 0x04,
+		0x01, 0x4B, 0x0F, 0x00, 0x07, 0x04}},
+	{340000, 0x01, 0x02,
+		{0x03, 0x53, 0x1A, 0x03, 0x00, 0x04,
+		0x03, 0x53, 0x1A, 0x03, 0x00, 0x04,
+		0x03, 0x53, 0x1A, 0x03, 0x00, 0x04,
+		0x01, 0x4B, 0x0F, 0x00, 0x07, 0x04}},
+	{150000, 0x00, 0x00,
+		{0x03, 0x43, 0x18, 0x03, 0x00, 0x04,
+		0x03, 0x43, 0x18, 0x03, 0x00, 0x04,
+		0x03, 0x43, 0x18, 0x03, 0x00, 0x04,
+		0x03, 0x43, 0x18, 0x03, 0x00, 0x04}},
+	{0, 0x00, 0x00,
+		{0x03, 0x43, 0x18, 0x03, 0x00, 0x04,
+		0x03, 0x43, 0x18, 0x03, 0x00, 0x04,
+		0x03, 0x43, 0x18, 0x03, 0x00, 0x04,
+		0x03, 0x43, 0x18, 0x03, 0x00, 0x04}}
+};
+
+static void it61620_hdmi_setup_afe(struct it61620 *it61620, int clock)
+{
+	int i;
+	unsigned int reg_ofset;
+	const struct it61620_hdmi_afe_setting *afe;
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_AFE30, 0x1f, 0x07);
+	regmap_write(it61620->tx_regmap, TX_REG_AFEE9, 0x10);
+	regmap_write(it61620->tx_regmap, TX_REG_AFE33, 0x00);
+	regmap_write(it61620->tx_regmap, TX_REG_AFE34, 0xe4);
+
+	regmap_write(it61620->tx_regmap, TX_REG_AFE35, 0x00);
+	regmap_write(it61620->tx_regmap, TX_REG_AFEE9, 0x10);
+
+	for (i = 0; i < sizeof(hdmi_afe); i++) {
+		if (clock > hdmi_afe[i].clock || hdmi_afe[i].clock == 0)
+			break;
+	}
+
+	afe = &hdmi_afe[i];
+
+	regmap_write(it61620->tx_regmap, TX_REG_AFE0E, 0xf0);
+
+	if (clock > 100000)
+		regmap_update_bits(it61620->tx_regmap, TX_REG_TXPLL_CTRL, 0x19, 0x08);
+	else
+		regmap_update_bits(it61620->tx_regmap, TX_REG_TXPLL_CTRL, 0x19, 0x11);
+
+	if (afe->h2on_pll) {
+		regmap_write(it61620->tx_regmap, TX_REG_AFE_XP, 0x48);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_AFE_XLC1, 0x87, 0x81);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_AFE_XLC2, 0x40, 0x00);
+		regmap_write(it61620->tx_regmap, TX_REG_AFE_XLC3, 0x32);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_XLC_7_CTRL, 0xff, 0xb6);
+	} else {
+		regmap_write(it61620->tx_regmap, TX_REG_AFE_XP, 0x00);
+	}
+
+	regmap_write(it61620->tx_regmap, TX_REG_AFE_DRV, 0x00);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXDRV_CTRL, 0x02, afe->hs);
+
+	for (i = 0, reg_ofset = 0x280; reg_ofset <= 0x297; reg_ofset++)
+		regmap_write(it61620->tx_regmap, reg_ofset, afe->afe_val[i++]);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_INT_MASK3, B_TMDS_STB_CHG,
+			   B_TMDS_STB_CHG);
+}
+
+static inline void it61620_hdmi_fire_afe(struct it61620 *it61620)
+{
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXDRV_CTRL, 0xf1, 0xf1);
+}
+
+static inline void it61620_hdmi_disable_afe(struct it61620 *it61620)
+{
+	regmap_update_bits(it61620->tx_regmap, TX_REG_TXDRV_CTRL, 0xf1, 0x00);
+}
+
+static int it61620_hdmi_hdcprd(struct it61620 *it61620, int offset, int bytenum)
+{
+	int ret = 0;
+
+	guard(mutex)(&it61620->ddc_lock);
+
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_COMMAND, DDC_COMMAND_FIFO_CLR);
+	usleep_range(1000, 2000);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_ADDR, DDC_HDCP_ADDR);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_OFFSET, offset);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_NUM_L, bytenum);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_NUM_H, (bytenum & 0x300) >> 8);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_COMMAND, DDC_COMMAND_BURST_R);
+
+	if (it61620_hdmi_ddc_wait(it61620) < 0) {
+		it61620_hdmi_ddc_abort(it61620);
+		ret = -EIO;
+		dev_dbg(it61620->dev, "ddc fail");
+	}
+	return ret;
+}
+
+static int it61620_hdmi_hdcp_ksvlist_rd(struct it61620 *it61620,
+					unsigned int bytenum, u8 *out)
+{
+	int ret = 0;
+	unsigned int i;
+	unsigned int count;
+
+	guard(mutex)(&it61620->ddc_lock);
+
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_COMMAND, DDC_COMMAND_FIFO_CLR);
+	usleep_range(1000, 2000);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_ADDR, DDC_HDCP_ADDR);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_OFFSET, 0x43);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_NUM_L, bytenum);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_NUM_H, (bytenum & 0x300) >> 8);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_COMMAND, DDC_COMMAND_BURST_R);
+
+	for (i = 0; i < bytenum;) {
+		if (it61620_hdmi_ddc_wait(it61620) < 0) {
+			it61620_hdmi_ddc_abort(it61620);
+			ret = -EIO;
+			dev_dbg(it61620->dev, "ddc fail");
+			break;
+		}
+
+		regmap_read(it61620->tx_regmap, TX_REG_DDC_FIFO_STS, &count);
+		count &= M_DDC_STAGE_NUM;
+		it61620_hdmi_get_ddc_fifo(it61620, (out + i), count);
+		i += count;
+	}
+
+	return ret;
+}
+
+static int it61620_hdmi_setup_sha1_input(struct it61620 *it61620, u8 *input)
+{
+	struct drm_device *drm = it61620->drm;
+	u8 bstatus[2];
+	int down_stream_count, count = 0;
+
+	it61620_hdmi_hdcprd(it61620, DRM_HDCP_DDC_BSTATUS, DRM_HDCP_BSTATUS_LEN);
+	regmap_bulk_read(it61620->tx_regmap, TX_REG_HDCP_BSTS_L, bstatus, 2);
+
+	down_stream_count = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
+	if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus[0]) ||
+	    DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus[1])) {
+		regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL3,
+				   (B_PAUSE | B_ENC_DIS), B_PAUSE);
+		return 0;
+	}
+
+	if (!down_stream_count ||
+	    down_stream_count > MAX_HDCP_DOWN_STREAM_COUNT) {
+		drm_dbg(drm, "HDCP down stream count Error %d",
+			down_stream_count);
+		return 0;
+	}
+	drm_dbg(drm, "down stream count %d\n", down_stream_count);
+
+	count = down_stream_count * DRM_HDCP_KSV_LEN;
+	it61620_hdmi_hdcp_ksvlist_rd(it61620, count, input);
+	if (drm_hdcp_check_ksvs_revoked(drm, input, down_stream_count) > 0)
+		return 0;
+
+	input[count++] = bstatus[0];
+	input[count++] = bstatus[1];
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_SHA_SEL, 0x70, 0x70);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL2, B_EN_M0_RD, B_EN_M0_RD);
+	regmap_bulk_read(it61620->tx_regmap, TX_REG_ANM0V1, &input[count], 8);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL2, B_EN_M0_RD, 0x00);
+
+	return count;
+}
+
+static bool it61620_hdmi_hdcp_part2_ksvlist_check(struct it61620 *it61620)
+{
+	struct drm_device *drm = it61620->drm;
+	u8 av[5][4], bv[5][4];
+	int i;
+
+	i = it61620_hdmi_setup_sha1_input(it61620, it61620->sha1_input);
+	if (i <= 0)
+		return false;
+
+	sha1(it61620->sha1_input, i, (u8 *)av);
+
+	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
+		it61620_hdmi_hdcprd(it61620, DRM_HDCP_DDC_V_PRIME(i),
+				    DRM_HDCP_V_PRIME_PART_LEN);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL2,
+				   B_EN_M0_RD, B_EN_M0_RD);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_SHA_SEL, 0x70, (i << 4));
+
+		regmap_bulk_read(it61620->tx_regmap, TX_REG_ANM0V1, bv[i],
+				 DRM_HDCP_V_PRIME_PART_LEN);
+	}
+
+	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++)
+		if (bv[i][3] != av[i][0] || bv[i][2] != av[i][1] ||
+		    bv[i][1] != av[i][2] || bv[i][0] != av[i][3])
+			break;
+
+	if (i == DRM_HDCP_V_PRIME_NUM_PARTS)
+		return true;
+
+	drm_dbg(drm, "V' Not match!!");
+	return false;
+}
+
+static unsigned int it61620_hdmi_hdcp_read_bcaps(struct it61620 *it61620)
+{
+	unsigned int val;
+
+	it61620_hdmi_hdcprd(it61620, DRM_HDCP_DDC_BCAPS, 1);
+	regmap_read(it61620->tx_regmap, TX_REG_HDCP_BCAPS, &val);
+	return val;
+}
+
+static void it61620_hdmi_hdcp_wait_ksv_list(struct it61620 *it61620)
+{
+	unsigned int bcaps = 0;
+
+	readx_poll_timeout(it61620_hdmi_hdcp_read_bcaps,
+			   it61620, bcaps,
+			   ((bcaps & B_KSV_READY) |
+			    (it61620->hdcp_state != CP_GOING)),
+			   2000,
+			   1000 * HDCP_KSV_LIST_TIMEOUT_MS);
+
+	if ((bcaps & B_KSV_READY) &&
+	    it61620_hdmi_hdcp_part2_ksvlist_check(it61620)) {
+		regmap_write(it61620->tx_regmap, TX_REG_HDCP_PRO, B_LIST_CHK_DONE);
+		return;
+	}
+	regmap_write(it61620->tx_regmap, TX_REG_HDCP_PRO,
+		     B_LIST_CHK_DONE | B_LIST_CHK_FAIL);
+}
+
+static inline void it61620_hdmi_enable_avmute(struct it61620 *it61620,
+					      bool enable)
+{
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDMI_CTRL2, B_EN_AVMUTE, enable);
+}
+
+static int it61620_hdmi_enable_hdcp(struct it61620 *it61620)
+{
+	struct drm_device *drm = it61620->drm;
+	unsigned int sts, auth_cs;
+
+	regmap_read(it61620->tx_regmap, TX_REG_V_STS, &sts);
+	if ((sts & B_TMDS_STABLE) != B_TMDS_STABLE) {
+		drm_dbg(drm, "TMDS not stable, stop hdcp %x", sts);
+		return false;
+	}
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL3, B_ENC_DIS, B_ENC_DIS);
+	usleep_range(1000, 2000);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_2_CTRL, 0x10, 0x10);
+	usleep_range(1000, 2000);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_2_CTRL, 0x10, 0x00);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_R0TM, 0xc0, 0x40);
+	regmap_write(it61620->tx_regmap, TX_REG_SHA_SEL, 0x00);
+
+	regmap_write(it61620->tx_regmap, TX_REG_AUTOMUTE, 0x00);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL2, B_AN_SEL, B_AN_SEL);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL2, B_EN_AN_GEN,
+			   B_EN_AN_GEN);
+	usleep_range(1000, 2000);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL2, B_EN_AN_GEN, 0x00);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL1, B_CPDESIRED,
+			   B_CPDESIRED);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL3, (B_PAUSE | B_ENC_DIS),
+			   0x00);
+
+	regmap_write(it61620->tx_regmap, TX_REG_HDCP_PRO, B_AUTH_FIRE);
+
+	/*
+	 * HDCP start requires 100ms for the first part of the authentication
+	 * protocol,20ms is used to wait for 61620 HW completion.
+	 */
+	if (wait_event_timeout(it61620->wq, !it61620->hpd, msecs_to_jiffies(120)))
+		return false;
+
+	regmap_read(it61620->tx_regmap, TX_REG_HDCP_AUTH_CS, &auth_cs);
+	auth_cs &= M_AUTH_CH;
+	if (auth_cs == CS_KSVLIST_CHK) {
+		it61620_hdmi_hdcp_wait_ksv_list(it61620);
+		usleep_range(1000, 2000);
+		regmap_read(it61620->tx_regmap, TX_REG_HDCP_AUTH_CS, &auth_cs);
+		auth_cs &= M_AUTH_CH;
+	}
+
+	if (auth_cs == CS_AUTH_DONE) {
+		it61620_hdmi_enable_avmute(it61620, false);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_INT1, B_INT_AUTH_F,
+				   B_INT_AUTH_F);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_INT_MASK1, B_INT_AUTH_F,
+				   B_INT_AUTH_F);
+		drm_dbg(drm, "auth done");
+		it61620->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+		drm_hdcp_update_content_protection(it61620->connector,
+						   it61620->hdcp_cp);
+		return true;
+	}
+
+	drm_dbg(drm, "auth fail");
+	it61620_hdmi_enable_avmute(it61620, true);
+	it61620_hdmi_ddc_abort(it61620);
+	return false;
+}
+
+static void it61620_hdmi_reset_hdcp(struct it61620 *it61620)
+{
+	regmap_update_bits(it61620->tx_regmap, TX_REG_INT_MASK1,
+			   (B_INT_AUTH_D | B_INT_AUTH_F), 0x00);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_INT_MASK2, B_KSV_CHECK, 0x00);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL3, B_ENC_DIS, B_ENC_DIS);
+	usleep_range(1000, 2000);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_2_CTRL, 0x18, 0x18);
+	usleep_range(1000, 2000);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_2_CTRL, 0x18, 0x00);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL1, B_CPDESIRED,
+			   B_CPDESIRED);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDCP_CTRL3,
+			   (B_PAUSE | B_ENC_DIS), B_PAUSE);
+	regmap_write(it61620->tx_regmap, TX_REG_AUTOMUTE, 0x00);
+}
+
+static bool it61620_hdmi_hdcp_is_ksv_valid(u8 *ksv)
+{
+	int i, ones = 0;
+
+	if ((ksv[4] == 0x93 && ksv[3] == 0x43 && ksv[2] == 0x5c &&
+	     ksv[1] == 0xde && ksv[0] == 0x23) ||
+	    (ksv[4] == 0x7d && ksv[3] == 0xb4 && ksv[2] == 0x21 &&
+	     ksv[1] == 0x37 && ksv[0] == 0x0b))
+		return false;
+
+	/* KSV has 20 1's and 20 0's */
+	for (i = 0; i < 5; i++)
+		ones += hweight8(ksv[i]);
+
+	return ones == 20;
+}
+
+static void it61620_hdmi_disable_hdcp(struct it61620 *it61620)
+{
+	it61620_hdmi_reset_hdcp(it61620);
+}
+
+static int it61620_hdmi_start_hdcp(struct it61620 *it61620)
+{
+	struct drm_device *drm = it61620->drm;
+	unsigned int rx_hdmi_mode;
+	u8 bksv[DRM_HDCP_KSV_LEN];
+	bool retcheck;
+
+	if (it61620_hdmi_hdcprd(it61620, DRM_HDCP_DDC_BSTATUS,
+				DRM_HDCP_BSTATUS_LEN) < 0) {
+		drm_dbg(drm, "read bstatus fail!\n");
+		return false;
+	}
+
+	regmap_read(it61620->tx_regmap, 0x7e, &rx_hdmi_mode);
+	rx_hdmi_mode = (rx_hdmi_mode & 0x10) >> 4;
+	if (it61620->is_hdmi != rx_hdmi_mode)
+		return false;
+
+	it61620_hdmi_hdcprd(it61620, DRM_HDCP_DDC_BKSV, DRM_HDCP_KSV_LEN);
+	regmap_bulk_read(it61620->tx_regmap, TX_REG_HDCP_BKSV1, bksv, 5);
+	retcheck = it61620_hdmi_hdcp_is_ksv_valid(bksv);
+	if (!retcheck) {
+		drm_dbg(drm, "ksv valid!\n");
+		return false;
+	}
+
+	if (drm_hdcp_check_ksvs_revoked(drm, bksv, 1) > 0)
+		return false;
+
+	return it61620_hdmi_enable_hdcp(it61620);
+}
+
+static void it61620_start_hdcp_work(struct it61620 *it61620)
+{
+	if (it61620->hdcp_cp == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+		return;
+
+	it61620->hdcp_state = CP_GOING;
+	queue_delayed_work(system_wq, &it61620->hdcp_work,
+			   msecs_to_jiffies(2400));
+}
+
+static void it61620_stop_hdcp_work(struct it61620 *it61620)
+{
+	it61620->hdcp_state = CP_NONE;
+	cancel_delayed_work_sync(&it61620->hdcp_work);
+	it61620_hdmi_disable_hdcp(it61620);
+}
+
+static void it61620_hdmi_irq_hdcp_auth_fail(struct it61620 *it61620)
+{
+	struct drm_device *drm = it61620->drm;
+
+	if (it61620->hdcp_state == CP_GOING) {
+		it61620_hdmi_enable_avmute(it61620, true);
+		drm_dbg(drm, "auth fail after done, set avmute");
+		it61620->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		drm_hdcp_update_content_protection(it61620->connector,
+						   it61620->hdcp_cp);
+		it61620_start_hdcp_work(it61620);
+	}
+}
+
+static void it61620_hdmi_irq_hpd(struct it61620 *it61620)
+{
+	it61620->hpd = it61620_hdmi_get_hpd_status(it61620);
+
+	if (!it61620->hpd) {
+		wake_up(&it61620->wq);
+		it61620_stop_hdcp_work(it61620);
+		it61620_hdmi_disable_afe(it61620);
+	} else if (it61620->video_state == it61620_VIDEO_ON) {
+		it61620_hdmi_fire_afe(it61620);
+		it61620_start_hdcp_work(it61620);
+	}
+}
+
+static void it61620_hdmi_irq_rxsen_chg(struct it61620 *it61620)
+{
+	unsigned int rxsen;
+
+	if (!it61620_hdmi_get_hpd_status(it61620))
+		return;
+
+	regmap_read(it61620->tx_regmap, TX_REG_V_STS, &rxsen);
+	rxsen &= B_RXSEN;
+	if (it61620->video_state == it61620_VIDEO_ON) {
+		if (rxsen) {
+			it61620_hdmi_fire_afe(it61620);
+			it61620_start_hdcp_work(it61620);
+		} else {
+			it61620_stop_hdcp_work(it61620);
+			it61620_hdmi_disable_afe(it61620);
+		}
+	}
+}
+
+static void it61620_hdmi_irq_tmds_stb_change(struct it61620 *it61620)
+{
+	unsigned int video_status;
+
+	regmap_read(it61620->tx_regmap, TX_REG_V_STS, &video_status);
+
+	if (video_status & B_TMDS_STABLE)
+		it61620_start_hdcp_work(it61620);
+}
+
+static void it61620_hdmi_interrupt_handler(struct it61620 *it61620)
+{
+	unsigned int int_status1, int_status2, int_status3;
+
+	regmap_read(it61620->tx_regmap, TX_REG_INT1, &int_status1);
+	regmap_read(it61620->tx_regmap, TX_REG_INT2, &int_status2);
+	regmap_read(it61620->tx_regmap, TX_REG_INT3, &int_status3);
+
+	regmap_write(it61620->tx_regmap, TX_REG_INT1, int_status1);
+	regmap_write(it61620->tx_regmap, TX_REG_INT2, int_status2);
+	regmap_write(it61620->tx_regmap, TX_REG_INT3, int_status3);
+
+	if (TX_REG_INT1 & B_HPD_CHG)
+		it61620_hdmi_irq_hpd(it61620);
+
+	if (TX_REG_INT1 & B_RXSEN_CHANGE)
+		it61620_hdmi_irq_rxsen_chg(it61620);
+
+	if (TX_REG_INT1 & B_INT_AUTH_F)
+		it61620_hdmi_irq_hdcp_auth_fail(it61620);
+
+	if (int_status3 & B_TMDS_STB_CHG)
+		it61620_hdmi_irq_tmds_stb_change(it61620);
+}
+
+static void it61620_hdmi_irq(struct it61620 *it61620)
+{
+	unsigned int reg_val;
+
+	regmap_read(it61620->tx_regmap, TX_REG_HDMITX_BANK, &reg_val);
+	if (reg_val & B_INT_EVENT_HDMI)
+		it61620_hdmi_interrupt_handler(it61620);
+}
+
+static void it61620_hdmi_audio_set_ncts(struct it61620 *it61620,
+					unsigned int sample_rate)
+{
+	unsigned int n, cts;
+
+	drm_hdmi_acr_get_n_cts(it61620->tmds_char_rate, sample_rate,
+			       &n, &cts);
+
+	regmap_write(it61620->tx_regmap, TX_REG_AUDPKT_N_0, n & 0xff);
+	regmap_write(it61620->tx_regmap, TX_REG_AUDPKT_N_1, (n >> 8) & 0xff);
+	regmap_write(it61620->tx_regmap, TX_REG_AUDPKT_N_2, (n >> 16) & 0x0f);
+
+	regmap_write(it61620->tx_regmap, TX_REG_AUDPKT_CTS_0, cts & 0xff);
+	regmap_write(it61620->tx_regmap, TX_REG_AUDPKT_CTS_1, (cts >> 8) & 0xff);
+	regmap_write(it61620->tx_regmap, TX_REG_AUDPKT_CTS_2, (cts >> 16) & 0x0f);
+}
+
+static void it61620_hdmi_avi_infoframe_set(struct it61620 *it61620,
+					   const u8 *buffer, size_t len)
+{
+	struct drm_device *drm = it61620->drm;
+	const u8 *ptr;
+	u8 i;
+
+	drm_dbg(drm, "avi info set\n");
+
+	/* fill PB */
+	ptr = buffer + HDMI_INFOFRAME_HEADER_SIZE;
+	for (i = 0; i < len - HDMI_INFOFRAME_HEADER_SIZE; i++)
+		regmap_write(it61620->tx_regmap, TX_REG_AVIINFO_DB01 + i, ptr[i]);
+	/* checksum */
+	regmap_write(it61620->tx_regmap, TX_REG_AVIINFO_DB14, buffer[3]);
+
+	/* Enable */
+	regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT1, (B_EN_AVI | B_AVI_RP),
+			   (B_EN_AVI | B_AVI_RP));
+}
+
+static void it61620_hdmi_spd_infoframe_set(struct it61620 *it61620,
+					   const u8 *buffer, size_t len)
+{
+	struct drm_device *drm = it61620->drm;
+	u8 i;
+	const u8 *ptr;
+
+	drm_dbg(drm, "spd info set\n");
+	regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT2,
+			   (B_EN_NULL | B_EN_NULL_RP), 0x00);
+
+	for (i = 0; i < 3; i++)
+		regmap_write(it61620->tx_regmap, TX_REG_NULLPKT_HB00 + i, buffer[i]);
+	ptr = buffer + 3;
+	for (i = 0; i < len - 3; i++)
+		regmap_write(it61620->tx_regmap, TX_REG_NULLPKT_PB00 + i, ptr[i]);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT2,
+			   (B_EN_NULL | B_EN_NULL_RP),
+			   (B_EN_NULL | B_EN_NULL_RP));
+}
+
+static void it61620_hdmi_vendor_infoframe_set(struct it61620 *it61620,
+					      const u8 *buffer, size_t len)
+{
+	struct drm_device *drm = it61620->drm;
+	u8 i;
+	const u8 *ptr;
+
+	drm_dbg(drm, "VSIF set\n");
+	regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT1,
+			   (B_EN_VSIF | B_EN_VSIF_RP), 0x00);
+
+	regmap_write(it61620->tx_regmap, TX_REG_VSIFPKT_HB02, buffer[2]);
+	ptr = buffer + 3;
+	for (i = 3; i < len - 3; i++)
+		regmap_write(it61620->tx_regmap, TX_REG_VSIFPKT_PB00 + i, ptr[i]);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT1,
+			   (B_EN_VSIF | B_EN_VSIF_RP),
+			   (B_EN_VSIF | B_EN_VSIF_RP));
+}
+
+static void it61620_hdmi_config_output(struct it61620 *it61620)
+{
+	struct drm_device *drm = it61620->drm;
+
+	it61620_hdmi_reset(it61620);
+	it61620_hdmi_poweron(it61620);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_HDMI_CTRL2, 0x70, 0x00);
+	if (it61620->is_hdmi) {
+		drm_dbg(drm, "HDMI\n");
+		regmap_update_bits(it61620->tx_regmap, TX_REG_HDMI_CTRL1,
+				   B_EN_HDMI_MODE, B_EN_HDMI_MODE);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT2,
+				   (B_EN_GEN | B_GEN_RP),
+				   (B_EN_GEN | B_GEN_RP));
+	} else {
+		drm_dbg(drm, "DVI\n");
+		regmap_update_bits(it61620->tx_regmap, TX_REG_HDMI_CTRL1,
+				   B_EN_HDMI_MODE, 0x00);
+		regmap_write(it61620->tx_regmap, TX_REG_EN_PKT2, 0x00);
+		regmap_write(it61620->tx_regmap, TX_REG_EN_PKT1, 0x00);
+	}
+
+	it61620_hdmi_enable_avmute(it61620, false);
+	it61620_hdmi_setup_afe(it61620, it61620->tmds_char_rate / 1000);
+	it61620_hdmi_fire_afe(it61620);
+	it61620->video_state = it61620_VIDEO_ON;
+}
+
+static int it61620_hdmi_audio_infoframe_set(struct it61620 *it61620,
+					    const u8 *buffer, size_t len)
+{
+	u8 i;
+	const u8 *ptr;
+
+	/* fill PB */
+	ptr = buffer + HDMI_INFOFRAME_HEADER_SIZE;
+	for (i = 0; i < len - HDMI_INFOFRAME_HEADER_SIZE; i++)
+		regmap_write(it61620->tx_regmap, TX_REG_AUDINFO_DB01 + i, ptr[i]);
+
+	regmap_write(it61620->tx_regmap, TX_REG_AUDINFO_DB06, buffer[3]);
+	regmap_write(it61620->tx_regmap, TX_REG_AUDINFO_DB07, 0x00);
+	regmap_write(it61620->tx_regmap, TX_REG_AUDINFO_DB08, 0x00);
+	regmap_write(it61620->tx_regmap, TX_REG_AUDINFO_DB09, 0x00);
+	regmap_write(it61620->tx_regmap, TX_REG_AUDINFO_DB10, 0x00);
+
+	/* Enable */
+	regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT1, (B_EN_AUD | B_EN_AUD_RP),
+			   (B_EN_AUD | B_EN_AUD_RP));
+	return 0;
+}
+
+static void it61620_hw_reset(struct it61620 *it61620)
+{
+	if (!it61620->gpiod_reset)
+		return;
+
+	gpiod_set_value_cansleep(it61620->gpiod_reset, 1);
+
+	usleep_range(10000, 20000);
+	gpiod_set_value_cansleep(it61620->gpiod_reset, 0);
+	usleep_range(10000, 20000);
+}
+
+static int it61620_enable_devices(struct it61620 *it61620)
+{
+	struct device *dev = &it61620->it61620_i2c->dev;
+	unsigned int device_id[5];
+	unsigned int i, vid, pid;
+
+	regmap_write(it61620->it61620_regmap, REG_CTRL_PW, 0x55);
+	regmap_write(it61620->it61620_regmap, REG_CTRL_PW, 0xaa);
+	regmap_write(it61620->it61620_regmap, REG_CTRL_PW, 0xc3);
+	regmap_write(it61620->it61620_regmap, REG_CTRL_PW, 0xa5);
+	usleep_range(1000, 2000);
+
+	for (i = 0; i < 5; i++)
+		regmap_read(it61620->mipirx_regmap, REG_VENDER_ID_L + i, &device_id[i]);
+
+	dev_dbg(dev, "IT61620 ver %02X", device_id[4]);
+
+	vid = (device_id[1] << 8) | device_id[0];
+	pid = (device_id[3] << 8) | device_id[2];
+
+	if (vid != it61620->chip_info->vid ||
+	    pid != it61620->chip_info->pid) {
+		dev_err(dev, "vid %X != %X",
+			vid, it61620->chip_info->vid);
+		dev_err(dev, "pid %X != %X",
+			pid, it61620->chip_info->pid);
+
+		return -ENODEV;
+	}
+
+	it61620->dev_ver = device_id[4];
+	return 0;
+}
+
+static void it61620_int_setup(struct it61620 *it61620)
+{
+	/* SET INT# to Active Low open-drain */
+	regmap_update_bits(it61620->mipirx_regmap, RX_REG_INT_POL, 0x08, 0x08);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_SYS_CONFIG, 0x0f, B_INT_MODE);
+	regmap_update_bits(it61620->tx_regmap, REG_TX_INT_CTRL, B_DIS_INT_OUTPUT, 0x00);
+}
+
+static void it61620_int_enable(struct it61620 *it61620)
+{
+	regmap_update_bits(it61620->tx_regmap, TX_REG_INT_MASK1, B_HPD_CHG, B_HPD_CHG);
+}
+
+static int it61620_reset_init(struct it61620 *it61620)
+{
+	it61620_hw_reset(it61620);
+
+	if (it61620_enable_devices(it61620) < 0)
+		return -ENODEV;
+
+	it61620_mipi_reset(it61620);
+	it61620_hdmi_reset(it61620);
+	it61620_int_setup(it61620);
+	it61620_int_enable(it61620);
+	it61620_hdmi_powerdown(it61620);
+
+	return 0;
+}
+
+static int it61620_poweron(struct it61620 *it61620)
+{
+	struct drm_device *drm = it61620->drm;
+	struct device *dev = it61620->dev;
+	int err;
+
+	if (it61620->powered) {
+		drm_dbg(drm, "Already powered on");
+		return 0;
+	}
+
+	err = regulator_enable(it61620->ivdd);
+	if (err) {
+		dev_err(dev, "Failed to enable IVDD: %d", err);
+		goto poweron_exit;
+	}
+
+	err = regulator_enable(it61620->ovdd1833);
+	if (err) {
+		dev_err(dev, "Failed to enable OVDD1833: %d", err);
+		goto disable_ivdd;
+	}
+
+	err = regulator_enable(it61620->ovdd33);
+	if (err) {
+		dev_err(dev, "Failed to enable OVDD33: %d", err);
+		goto disable_ovdd1833;
+	}
+
+	gpiod_set_value_cansleep(it61620->gpiod_reset, 1);
+	usleep_range(10000, 20000);
+	gpiod_set_value_cansleep(it61620->gpiod_reset, 0);
+	usleep_range(10000, 20000);
+
+	err = it61620_reset_init(it61620);
+	if (err < 0)
+		goto disable_ovdd33;
+
+	it61620->powered = true;
+	it61620->hpd = it61620_hdmi_get_hpd_status(it61620);
+	if (it61620->it61620_i2c->irq) {
+		enable_irq(it61620->it61620_i2c->irq);
+		drm_dbg(drm, "enable irq %d\n",
+			it61620->it61620_i2c->irq);
+	}
+	drm_dbg(drm, "it61620 poweron end\n");
+	return 0;
+
+disable_ovdd33:
+	regulator_disable(it61620->ovdd33);
+disable_ovdd1833:
+	regulator_disable(it61620->ovdd1833);
+disable_ivdd:
+	regulator_disable(it61620->ivdd);
+poweron_exit:
+	return err;
+}
+
+static int it61620_poweroff(struct it61620 *it61620)
+{
+	struct drm_device *drm = it61620->drm;
+	struct device *dev = it61620->dev;
+	int err;
+
+	if (!it61620->powered) {
+		drm_dbg(drm, "Already powered off");
+		return 0;
+	}
+
+	if (it61620->it61620_i2c->irq) {
+		disable_irq(it61620->it61620_i2c->irq);
+		drm_dbg(drm, "disable irq %d\n",
+			it61620->it61620_i2c->irq);
+	}
+
+	gpiod_set_value_cansleep(it61620->gpiod_reset, 1);
+
+	err = regulator_disable(it61620->ovdd33);
+	if (err)
+		dev_err(dev, "Failed to disable ovdd33: %d", err);
+
+	err = regulator_disable(it61620->ivdd);
+	if (err)
+		dev_err(dev, "Failed to disable IVDD: %d", err);
+
+	usleep_range(2000, 3000);
+
+	err = regulator_disable(it61620->ovdd1833);
+	if (err)
+		dev_err(dev, "Failed to disable ovdd1833: %d", err);
+
+	it61620->powered = false;
+	it61620->hpd = false;
+	it61620->connector_status = connector_status_disconnected;
+	drm_dbg(drm, "it61620 poweroff\n");
+
+	return 0;
+}
+
+static void it61620_config_default(struct it61620 *it61620)
+{
+	struct it61620_mipirx *mipirx = &it61620->mipirx_config;
+
+	mipirx->lane_num = 4;
+	mipirx->pn_swap = 0;
+	mipirx->lane_swap = 0;
+	it61620->connector_status = connector_status_disconnected;
+	it61620->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+	it61620->i2s_input_format = I2S_INPUT_FORMAT_STANDARD;
+}
+
+static int it61620_get_edid_block(void *data, u8 *buf, unsigned int block,
+				  size_t len)
+{
+	struct it61620 *it61620 = data;
+	unsigned int edid_offset;
+	unsigned int cnt;
+	unsigned int i;
+	int ret = 0;
+
+	if (len > EDID_LENGTH)
+		return -EINVAL;
+
+	guard(mutex)(&it61620->ddc_lock);
+
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_COMMAND, DDC_COMMAND_FIFO_CLR);
+
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_ADDR, DDC_EDID_ADDR);
+	regmap_write(it61620->tx_regmap, TX_REG_DDC_SEGMENT, block / 2);
+
+	cnt = 0;
+	edid_offset = block * 128;
+
+	for (i = 0; i < EDID_LENGTH; i += EDID_R_BURST_NUM,
+	     edid_offset += EDID_R_BURST_NUM,
+	     cnt += EDID_R_BURST_NUM) {
+		regmap_write(it61620->tx_regmap, TX_REG_DDC_OFFSET, edid_offset);
+		regmap_write(it61620->tx_regmap, TX_REG_DDC_SEGMENT, block >> 1);
+		regmap_write(it61620->tx_regmap, TX_REG_DDC_NUM_L, EDID_R_BURST_NUM);
+		regmap_write(it61620->tx_regmap, TX_REG_DDC_NUM_H,
+			     (EDID_R_BURST_NUM >> 8));
+		regmap_write(it61620->tx_regmap, TX_REG_DDC_COMMAND,
+			     DDC_COMMAND_EDID_RD);
+
+		if (it61620_hdmi_ddc_wait(it61620) < 0) {
+			it61620_hdmi_ddc_abort(it61620);
+			ret = -EIO;
+			break;
+		}
+
+		it61620_hdmi_get_ddc_fifo(it61620, &buf[cnt], EDID_R_BURST_NUM);
+	}
+
+	return ret;
+}
+
+static void it61620_enable_audio(struct it61620 *it61620)
+{
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_2_CTRL, 0x01, 0x00);
+}
+
+static void it61620_disable_audio(struct it61620 *it61620)
+{
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_2_CTRL, 0x01, 0x01);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_LINK_CTRL0,
+			   B_EN_AUDIO_MUTE, B_EN_AUDIO_MUTE);
+}
+
+static irqreturn_t it61620_int_threaded_handler(int unused, void *data)
+{
+	struct it61620 *it61620 = data;
+	struct device *dev = it61620->dev;
+
+	pm_runtime_get_sync(dev);
+
+	it61620_mipi_irq_handler(it61620);
+	it61620_hdmi_irq(it61620);
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
+	return IRQ_HANDLED;
+}
+
+static void it61620_audio_update_hw_params(struct it61620 *it61620,
+					   struct hdmi_codec_daifmt *fmt,
+					   struct hdmi_codec_params *hparms)
+{
+	struct drm_device *drm = it61620->drm;
+	u8 audsrc, sample_rate_val, sample_width;
+	bool is_lpcm;
+
+	switch (hparms->sample_rate) {
+	case 32000:
+		sample_rate_val = SAMPLE_RATE_32K;
+		break;
+	case 44100:
+		sample_rate_val = SAMPLE_RATE_44_1K;
+		break;
+	case 48000:
+		sample_rate_val = SAMPLE_RATE_48K;
+		break;
+	case 88200:
+		sample_rate_val = SAMPLE_RATE_88_2K;
+		break;
+	case 96000:
+		sample_rate_val = SAMPLE_RATE_96K;
+		break;
+	case 176400:
+		sample_rate_val = SAMPLE_RATE_176_4K;
+		break;
+	case 192000:
+		sample_rate_val = SAMPLE_RATE_192K;
+		break;
+	}
+
+	switch (hparms->sample_width) {
+	case 16:
+		sample_width = WORD_LENGTH_16BIT;
+		break;
+	case 20:
+		sample_width = WORD_LENGTH_20BIT;
+		break;
+	case 24:
+		sample_width = WORD_LENGTH_24BIT;
+		break;
+	}
+
+	switch (fmt->fmt) {
+	case HDMI_I2S:
+	case HDMI_SPDIF:
+		break;
+	default:
+		return;
+	}
+
+	is_lpcm = !(hparms->iec.status[0] & IEC958_AES0_NONAUDIO);
+	it61620_hdmi_audio_set_ncts(it61620, hparms->sample_rate);
+
+	drm_dbg(drm, "sample rate %d", sample_rate_val);
+	drm_dbg(drm, "sample width %d", sample_width);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_AUD_FMT, 0x7f,
+			   (sample_width << 5) | it61620->i2s_input_format);
+	if (fmt->fmt == HDMI_SPDIF) {
+		drm_dbg(drm, "SPDIF");
+		regmap_write(it61620->tx_regmap, TX_REG_AUD_FIFO1, 0x00);
+		regmap_write(it61620->tx_regmap, TX_REG_AUD_FIFO2, 0x00);
+		regmap_update_bits(it61620->tx_regmap, TX_REG_AUD_SPDIF, 0x0f, 0x02);
+		regmap_write(it61620->tx_regmap, TX_REG_AUD_CTRL, 0X01);
+	} else {
+		drm_dbg(drm, "I2S");
+		regmap_write(it61620->tx_regmap, TX_REG_AUD_FIFO1, 0x10);
+		regmap_write(it61620->tx_regmap, TX_REG_AUD_FIFO2, 0x32);
+		regmap_write(it61620->tx_regmap, TX_REG_AUD_CTRL, 0X00);
+	}
+
+	if (is_lpcm)
+		regmap_write(it61620->tx_regmap, TX_REG_AUD_STS1, 0x00);
+	else
+		regmap_write(it61620->tx_regmap, TX_REG_AUD_STS1, B_EN_AUD_NLPCM);
+	regmap_write(it61620->tx_regmap, TX_REG_AUD_STS2, sample_rate_val);
+	regmap_write(it61620->tx_regmap, TX_REG_AUD_STS3,
+		     ((~(sample_rate_val << 4)) & 0xf0) + 0x0B);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_AUD_CTS, 0x08, 0x08);
+
+	switch (hparms->channels) {
+	case 1:
+	case 2:
+		audsrc = 0x01;
+		break;
+	case 3:
+	case 4:
+		audsrc = 0x03;
+		break;
+	case 5:
+	case 6:
+		audsrc = 0x07;
+		break;
+	case 7:
+	case 8:
+		audsrc = 0x0f;
+		break;
+	}
+
+	regmap_write(it61620->tx_regmap, TX_REG_EN_AUDIO, audsrc);
+
+	regmap_update_bits(it61620->tx_regmap, TX_REG_LINK_CTRL0, B_EN_AUDIO_MUTE, 0x00);
+	regmap_update_bits(it61620->tx_regmap, TX_REG_RESET_2_CTRL, 0x01, 0x00);
+}
+
+static void it61620_hdcp_work(struct work_struct *work)
+{
+	struct it61620 *it61620 = container_of(work, struct it61620,
+					       hdcp_work.work);
+	it61620_hdmi_reset_hdcp(it61620);
+	if (!it61620_hdmi_start_hdcp(it61620) &&
+	    it61620->hdcp_state == CP_GOING) {
+		it61620_hdmi_disable_hdcp(it61620);
+		it61620_start_hdcp_work(it61620);
+	}
+}
+
+static int it61620_i2c_and_regmap_init(struct i2c_client *client,
+				       struct it61620 *it61620)
+{
+	struct device *dev = it61620->dev;
+
+	it61620->it61620_i2c = client;
+
+	it61620->tx_i2c = devm_i2c_new_dummy_device(dev,
+						    client->adapter,
+						    TX_I2C_ADDRESS);
+	if (IS_ERR(it61620->tx_i2c))
+		return dev_err_probe(dev, PTR_ERR(it61620->tx_i2c),
+				     "failed to create TX dummy i2c device at 0x%02x\n",
+				     TX_I2C_ADDRESS);
+
+	it61620->mipirx_i2c = devm_i2c_new_dummy_device(dev,
+							client->adapter,
+							MIPIRX_I2C_ADDRESS);
+	if (IS_ERR(it61620->mipirx_i2c))
+		return dev_err_probe(dev, PTR_ERR(it61620->mipirx_i2c),
+				     "failed to create MIPI dummy i2c device at 0x%02x\n",
+				     MIPIRX_I2C_ADDRESS);
+
+	it61620->it61620_regmap = devm_regmap_init_i2c(it61620->it61620_i2c,
+						       &it61620_regmap_config);
+	if (IS_ERR(it61620->it61620_regmap))
+		return dev_err_probe(dev, PTR_ERR(it61620->it61620_regmap),
+				     "failed to init I2C regmap for it61620\n");
+
+	it61620->tx_regmap = devm_regmap_init_i2c(it61620->tx_i2c,
+						  &it61620_tx_regmap_config);
+	if (IS_ERR(it61620->tx_regmap))
+		return dev_err_probe(dev, PTR_ERR(it61620->tx_regmap),
+				     "failed to init I2C regmap for TX\n");
+
+	it61620->mipirx_regmap = devm_regmap_init_i2c(it61620->mipirx_i2c,
+						      &it61620_mipi_regmap_config);
+	if (IS_ERR(it61620->mipirx_regmap))
+		return dev_err_probe(dev, PTR_ERR(it61620->mipirx_regmap),
+				     "failed to init I2C regmap for MIPI\n");
+	return 0;
+}
+
+static int it61620_attach_dsi(struct it61620 *it61620,
+			      struct mipi_dsi_host *host)
+{
+	struct device *dev = it61620->dev;
+	struct mipi_dsi_device *dsi;
+	const struct mipi_dsi_device_info info = {"it61620",
+						  0,
+						  dev->of_node};
+
+	dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
+	if (IS_ERR(dsi))
+		return dev_err_probe(dev, PTR_ERR(dsi), "failed to create dsi device\n");
+
+	it61620->dsi = dsi;
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
+			  MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+
+	return devm_mipi_dsi_attach(dev, dsi);
+}
+
+static void it61620_detach_dsi(struct it61620 *it61620)
+{
+	if (!it61620->dsi)
+		return;
+
+	mipi_dsi_detach(it61620->dsi);
+}
+
+static unsigned int it61620_parse_dt(struct it61620 *it61620)
+{
+	struct device *dev = it61620->dev;
+	struct device_node *np = it61620->dev->of_node;
+	int num_lanes;
+
+	if (!dev->of_node)
+		return -EINVAL;
+
+	num_lanes = drm_of_get_data_lanes_count_ep(np, 0, -1, 1, 4);
+	if (num_lanes < 0)
+		num_lanes = 4;
+	it61620->mipirx_config.lane_num = num_lanes;
+
+	it61620->next_bridge = devm_drm_of_get_bridge(dev, np, 1, -1);
+	if (IS_ERR(it61620->next_bridge))
+		return dev_err_probe(dev, PTR_ERR(it61620->next_bridge),
+				     "failed to get next bridge\n");
+
+	return 0;
+}
+
+static int it61620_init_power(struct it61620 *it61620)
+{
+	struct device *dev = it61620->dev;
+
+	it61620->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(it61620->gpiod_reset))
+		return dev_err_probe(dev, PTR_ERR(it61620->gpiod_reset),
+				     "gpiod_reset not found\n");
+
+	it61620->ivdd = devm_regulator_get(dev, "ivdd");
+	if (IS_ERR(it61620->ivdd))
+		return dev_err_probe(dev, PTR_ERR(it61620->ivdd),
+				     "ivdd regulator not found\n");
+
+	it61620->ovdd1833 = devm_regulator_get(dev, "ovdd1833");
+	if (IS_ERR(it61620->ovdd1833))
+		return dev_err_probe(dev, PTR_ERR(it61620->ovdd1833),
+				     "ovdd1833 regulator not found\n");
+
+	it61620->ovdd33 = devm_regulator_get(dev, "ovdd");
+	if (IS_ERR(it61620->ovdd33))
+		return dev_err_probe(dev, PTR_ERR(it61620->ovdd33),
+				     "ovdd33 regulator not found\n");
+
+	return 0;
+}
+
+static inline int __maybe_unused it61620_pm_bridge_suspend(struct device *dev)
+{
+	struct it61620 *it61620 = dev_get_drvdata(dev);
+
+	it61620_poweroff(it61620);
+
+	return 0;
+}
+
+static inline int __maybe_unused it61620_pm_bridge_resume(struct device *dev)
+{
+	struct it61620 *it61620 = dev_get_drvdata(dev);
+
+	return it61620_poweron(it61620);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(it61620_bridge_pm_ops,
+				 it61620_pm_bridge_suspend,
+				 it61620_pm_bridge_resume, NULL);
+
+static int it61620_bridge_attach(struct drm_bridge *bridge,
+				 struct drm_encoder *encoder,
+				 enum drm_bridge_attach_flags flags)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+	struct drm_device *drm = bridge->dev;
+
+	it61620->drm = drm;
+
+	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+		drm_dbg(drm,
+			"DRM_BRIDGE_ATTACH_NO_CONNECTOR must be supplied");
+		return -EINVAL;
+	}
+
+	return drm_bridge_attach(bridge->encoder, it61620->next_bridge,
+				bridge, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+}
+
+static enum drm_mode_status
+it61620_bridge_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
+					 const struct drm_display_mode *mode,
+					 unsigned long long tmds_rate)
+{
+	/* 297 MHz for 4k@30 8-bit */
+	if (tmds_rate > 297000000)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static void it61620_bridge_hpd_notify(struct drm_bridge *bridge,
+				      struct drm_connector *connector,
+				      enum drm_connector_status status)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+	struct drm_device *drm = it61620->drm;
+	struct device *dev = it61620->dev;
+	int hpd, ret;
+
+	hpd = (status == connector_status_connected) ? 1 : 0;
+
+	if (it61620->connector_status == status) {
+		drm_dbg(drm, "GPIO hpd status NO change %d", hpd);
+		return;
+	}
+
+	drm_dbg(drm, "GPIO hpd status change %d->%d",
+		!hpd, hpd);
+
+	it61620->connector_status = status;
+	if (hpd) {
+		drm_dbg(drm, "HPD_GPIO get to wake up");
+		ret = pm_runtime_get_sync(dev);
+		if (ret < 0)
+			dev_err(dev,
+				"pm_runtime_get_sync error %d", ret);
+	} else {
+		drm_dbg(drm, "HPD_GPIO put to sleep");
+		pm_runtime_mark_last_busy(dev);
+		pm_runtime_put_autosuspend(dev);
+	}
+}
+
+static void it61620_bridge_atomic_pre_enable(struct drm_bridge *bridge,
+					     struct drm_atomic_state *state)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+	struct device *dev = it61620->dev;
+
+	pm_runtime_get_sync(dev);
+}
+
+static void it61620_bridge_atomic_enable(struct drm_bridge *bridge,
+					 struct drm_atomic_state *state)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+	struct drm_crtc_state *crtc_state;
+	struct drm_connector_state *conn_state;
+	struct drm_display_mode *adj_mode;
+	struct drm_connector *connector;
+
+	connector = drm_atomic_get_new_connector_for_encoder(state,
+							     bridge->encoder);
+
+	if (!connector)
+		return;
+	it61620->connector = connector;
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+
+	it61620->hdcp_cp = conn_state->content_protection;
+	adj_mode = &crtc_state->adjusted_mode;
+
+	/*
+	 * Keep the video pixel clock for later N/CTS calculation and
+	 * HDMI AFE configuration
+	 */
+	it61620->tmds_char_rate = conn_state->hdmi.tmds_char_rate;
+	it61620->is_hdmi = connector->display_info.is_hdmi;
+	it61620->en_audio = connector->display_info.has_audio;
+	drm_dbg(it61620->drm, "%s mode, monitor %s support audio",
+		it61620->is_hdmi ? "HDMI" : "DVI",
+		it61620->en_audio ? "" : "not ");
+
+	drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
+	it61620_mipi_set_d2v_video_timing(it61620, adj_mode);
+	it61620_hdmi_config_output(it61620);
+}
+
+static void it61620_bridge_atomic_disable(struct drm_bridge *bridge,
+					  struct drm_atomic_state *state)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	if (!it61620->powered)
+		return;
+
+	it61620_hdmi_enable_avmute(it61620, true);
+	/* wait at least one frame for AVMute to take effect*/
+	msleep(45);
+	it61620_stop_hdcp_work(it61620);
+	if (it61620->hdcp_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+		drm_hdcp_update_content_protection(it61620->connector,
+						   DRM_MODE_CONTENT_PROTECTION_DESIRED);
+		it61620->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+	}
+	it61620_hdmi_disable_afe(it61620);
+	it61620_hdmi_powerdown(it61620);
+	it61620->video_state = it61620_VIDEO_OFF;
+}
+
+static void it61620_bridge_atomic_post_disable(struct drm_bridge *bridge,
+					       struct drm_atomic_state *state)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+	struct device *dev = it61620->dev;
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+}
+
+static const struct drm_edid *it61620_bridge_edid_read(struct drm_bridge *bridge,
+						       struct drm_connector *connector)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+	struct device *dev = it61620->dev;
+	const struct drm_edid *edid;
+
+	edid = drm_edid_read_custom(connector, it61620_get_edid_block, it61620);
+
+	if (!edid)
+		dev_dbg(dev, "failed to get edid!");
+
+	return edid;
+}
+
+static int it61620_bridge_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	return regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT1,
+				  (B_EN_AVI | B_AVI_RP), 0x00);
+}
+
+static int it61620_bridge_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	return regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT2,
+				  (B_EN_NULL | B_EN_NULL_RP), 0x00);
+}
+
+static int it61620_bridge_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	return regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT1,
+				  (B_EN_VSIF | B_EN_VSIF_RP), 0x00);
+}
+
+static int it61620_bridge_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	return regmap_update_bits(it61620->tx_regmap, TX_REG_EN_PKT1,
+				  (B_EN_AUD | B_EN_AUD_RP), 0x00);
+}
+
+static int it61620_bridge_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
+						   const u8 *buffer, size_t len)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	it61620_bridge_hdmi_clear_hdmi_infoframe(bridge);
+	it61620_hdmi_avi_infoframe_set(it61620, buffer, len);
+
+	return 0;
+}
+
+static int it61620_bridge_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
+						   const u8 *buffer, size_t len)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	it61620_hdmi_spd_infoframe_set(it61620, buffer, len);
+
+	return 0;
+}
+
+static int it61620_bridge_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
+						    const u8 *buffer, size_t len)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	it61620_hdmi_vendor_infoframe_set(it61620, buffer, len);
+
+	return 0;
+}
+
+static int it61620_bridge_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
+						     const u8 *buffer, size_t len)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	it61620_bridge_hdmi_clear_audio_infoframe(bridge);
+	it61620_hdmi_audio_infoframe_set(it61620, buffer, len);
+
+	return 0;
+}
+
+static int it61620_bridge_hdmi_audio_startup(struct drm_bridge *bridge,
+					     struct drm_connector *connector)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	it61620_enable_audio(it61620);
+	return 0;
+}
+
+static int it61620_bridge_hdmi_audio_prepare(struct drm_bridge *bridge,
+					     struct drm_connector *connector,
+					     struct hdmi_codec_daifmt *fmt,
+					     struct hdmi_codec_params *hparms)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	it61620_audio_update_hw_params(it61620, fmt, hparms);
+
+	return drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
+								       &hparms->cea);
+}
+
+static void it61620_bridge_hdmi_audio_shutdown(struct drm_bridge *bridge,
+					       struct drm_connector *connector)
+{
+	struct it61620 *it61620 = bridge_to_it61620(bridge);
+
+	drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector);
+
+	it61620_disable_audio(it61620);
+}
+
+static const struct drm_bridge_funcs it61620_bridge_funcs = {
+	.attach = it61620_bridge_attach,
+	.hpd_notify = it61620_bridge_hpd_notify,
+
+	.atomic_pre_enable = it61620_bridge_atomic_pre_enable,
+	.atomic_enable = it61620_bridge_atomic_enable,
+	.atomic_disable = it61620_bridge_atomic_disable,
+	.atomic_post_disable = it61620_bridge_atomic_post_disable,
+	.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,
+
+	.edid_read = it61620_bridge_edid_read,
+	.hdmi_tmds_char_rate_valid = it61620_bridge_hdmi_tmds_char_rate_valid,
+
+	.hdmi_clear_audio_infoframe = it61620_bridge_hdmi_clear_audio_infoframe,
+	.hdmi_write_audio_infoframe = it61620_bridge_hdmi_write_audio_infoframe,
+	.hdmi_clear_avi_infoframe = it61620_bridge_hdmi_clear_avi_infoframe,
+	.hdmi_write_avi_infoframe = it61620_bridge_hdmi_write_avi_infoframe,
+	.hdmi_clear_spd_infoframe = it61620_bridge_hdmi_clear_spd_infoframe,
+	.hdmi_write_spd_infoframe = it61620_bridge_hdmi_write_spd_infoframe,
+	.hdmi_clear_hdmi_infoframe = it61620_bridge_hdmi_clear_hdmi_infoframe,
+	.hdmi_write_hdmi_infoframe = it61620_bridge_hdmi_write_hdmi_infoframe,
+
+	.hdmi_audio_startup = it61620_bridge_hdmi_audio_startup,
+	.hdmi_audio_prepare = it61620_bridge_hdmi_audio_prepare,
+	.hdmi_audio_shutdown = it61620_bridge_hdmi_audio_shutdown,
+};
+
+static int it61620_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct device_node *np = dev->of_node;
+	struct mipi_dsi_host *host;
+	struct it61620 *it61620;
+	int ret = 0;
+
+	it61620 = devm_drm_bridge_alloc(dev, struct it61620, bridge,
+					&it61620_bridge_funcs);
+	if (IS_ERR(it61620))
+		return PTR_ERR(it61620);
+
+	it61620->dev = dev;
+	it61620->chip_info = of_device_get_match_data(dev);
+
+	host = drm_of_get_dsi_bus(dev);
+	if (IS_ERR(host))
+		return dev_err_probe(dev, PTR_ERR(host),
+				     "failed to find dsi host\n");
+
+	ret = it61620_i2c_and_regmap_init(client, it61620);
+	if (ret < 0)
+		return ret;
+
+	i2c_set_clientdata(client, it61620);
+
+	ret = it61620_init_power(it61620);
+	if (ret < 0)
+		return ret;
+
+	it61620_config_default(it61620);
+
+	ret = it61620_parse_dt(it61620);
+	if (ret < 0)
+		return ret;
+
+	if (!client->irq)
+		return dev_err_probe(dev, -ENODEV,
+				     "Failed to get INTP IRQ\n");
+
+	ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+					it61620_int_threaded_handler,
+					IRQF_TRIGGER_LOW | IRQF_ONESHOT |
+					IRQF_NO_AUTOEN,
+					"it61620-intp", it61620);
+	if (ret < 0)
+		return dev_err_probe(dev, ret,
+				     "failed to request INTP threaded IRQ\n");
+
+	INIT_DELAYED_WORK(&it61620->hdcp_work, it61620_hdcp_work);
+	init_waitqueue_head(&it61620->wq);
+
+	mutex_init(&it61620->ddc_lock);
+
+	pm_runtime_enable(dev);
+	pm_runtime_set_autosuspend_delay(dev, 1000);
+	pm_runtime_use_autosuspend(dev);
+
+	it61620->bridge.of_node = np;
+	/*
+	 * Although IT61620 has an HPD pin, hotplug detection is handled by the
+	 * system connector. The bridge only receives HPD status notifications,
+	 * and the HPD pin is used solely for short pulses during HDCP
+	 * authentication.
+	 *
+	 * Therefore, this bridge does not implement OP_HPD or OP_DETECT, as it
+	 * does not originate or determine hotplug or connection status.
+	 */
+	it61620->bridge.ops = DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HDMI |
+			      DRM_BRIDGE_OP_HDMI_AUDIO;
+	it61620->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+	it61620->bridge.support_hdcp = true;
+	it61620->bridge.vendor = "ITE";
+	it61620->bridge.product = "IT61620";
+	it61620->bridge.hdmi_audio_dev = dev;
+	it61620->bridge.hdmi_audio_max_i2s_playback_channels = 8;
+	it61620->bridge.hdmi_audio_spdif_playback = false;
+	it61620->bridge.hdmi_audio_dai_port = 2;
+
+	ret = devm_drm_bridge_add(dev, &it61620->bridge);
+	if (ret < 0)
+		return dev_err_probe(dev, ret,
+				     "failed to add drm bridge\n");
+
+	ret = it61620_attach_dsi(it61620, host);
+	if (ret < 0)
+		return dev_err_probe(dev, ret,
+				     "failed to attach to DSI host\n");
+
+	return 0;
+}
+
+static void it61620_remove(struct i2c_client *client)
+{
+	struct it61620 *it61620 = i2c_get_clientdata(client);
+	struct device *dev = it61620->dev;
+
+	disable_irq(client->irq);
+	pm_runtime_disable(dev);
+	it61620_detach_dsi(it61620);
+
+	mutex_destroy(&it61620->ddc_lock);
+}
+
+static const struct it6162_chip_info it61620_chip_info = {
+	.vid = 0x4954,
+	.pid = 0x6152,
+};
+
+static const struct of_device_id it61620_dt_ids[] = {
+	{ .compatible = "ite,it61620", .data = &it61620_chip_info},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, it61620_dt_ids);
+
+static const struct i2c_device_id it61620_i2c_ids[] = {
+	{ "it61620", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, it61620_i2c_ids);
+
+static struct i2c_driver it61620_driver = {
+	.driver = {
+		.name = "it61620",
+		.of_match_table = it61620_dt_ids,
+		.pm = &it61620_bridge_pm_ops,
+	},
+	.probe = it61620_probe,
+	.remove = it61620_remove,
+	.id_table = it61620_i2c_ids,
+};
+
+module_i2c_driver(it61620_driver);
+
+MODULE_AUTHOR("Pet Weng <pet.weng@ite.com.tw>");
+MODULE_AUTHOR("Hermes Wu <Hermes.Wu@ite.com.tw>");
+MODULE_DESCRIPTION("it61620 MIPI to HDMI driver");
+MODULE_LICENSE("GPL");

-- 
2.34.1


^ permalink raw reply related

* [PATCH v9 0/2] Add ITE IT61620 MIPI DSI to HDMI bridge driver
From: Pet Weng @ 2026-03-27  8:02 UTC (permalink / raw)
  To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: dri-devel, devicetree, linux-kernel, Hermes Wu, Kenneth Hung,
	Pet Weng, Jau-chih Tseng, Pin-yen Lin, Krzysztof Kozlowski,
	Dmitry Baryshkov

This patch series adds support for the ITE IT61620 MIPI DSI to HDMI 
bridge chip.

The IT61620 is an I2C-controlled bridge that receives MIPI DSI input 
and outputs HDMI signals. A single-port MIPI DSI input is converted to 
an HDMI 1.4 output. This series introduces:
- A device tree binding YAML file describing the hardware
- A new DRM bridge driver implementing the basic functionality

Signed-off-by: Pet Weng <pet.weng@ite.com.tw>
---
Changes in v9:
- Fix commit message wrapping to follow kernel style
- Run checkpatch.pl and address reported issues
- Restore Reviewed-by from Krzysztof as the change is non-functional
- Link to v8: https://lore.kernel.org/r/20260320-it61620-0714-v8-0-0e70271cf5a9@ite.com.tw

Changes in v8:
- dt-binding:
 1. Clarify the hardware differences between IT6162 and IT61620 in the
    description (IT61620 is single-port and lacks an internal MCU). 	[Krzysztof]
 2. Dropped Reviewed-by from Krzysztof due to description changes.
- Call drm_atomic_helper_connector_hdmi_clear_audio_infoframe() in audio
  shutdown path								[Dmitry]
- Link to v7: https://lore.kernel.org/r/20260313-it61620-0714-v7-0-36a16dc036d6@ite.com.tw

Changes in v7:
- The dt-bindings were previously reviewed by Krzysztof Kozlowski.
- drm/bridge:								[Dmitry]
 1. drop redundant register access wrappers and use regmap APIs directly
 2. use drm_dbg_kms() instead of drm_dbg() when printing display timing information
 3. use drm_display_mode directly for video timing
 4. add helper for writing 16-bit timing registers
 5. simplify HDMI interrupt handling
 6. add mono audio support
 7. program audio parameters directly
 8. inline audio infoframe disable logic
- MAINTAINERS: squash to driver patch					[Dmitry]
- Link to v6: https://lore.kernel.org/r/20260130-it61620-0714-v6-0-70afa65923b5@ite.com.tw

Changes in v6:
- In patch 1								[Luca] 
 1. Fix a typo in the commit message.
 2. Remove redundant assignment of bridge.funcs, which is already set by 
    devm_drm_bridge_alloc().
- Link to v5: https://lore.kernel.org/r/20251222-it61620-0714-v5-0-afb6479ad3ca@ite.com.tw

Changes in v5:
- Fix dt_binding_check errors by adding missing unevaluatedProperties constraints
  for port and endpoint nodes in the device tree binding.		[Rob]
- Link to v4: https://lore.kernel.org/r/20251216-it61620-0714-v4-0-9d2fea7847ae@ite.com.tw

Changes in v4:
- In patch 1								[Krzysztof]
 1. Remove redundant "description" fields from interrupts and regulators
 2. Drop pinctrl-names and pinctrl-0; driver does not require them
 3. Remove port/endpoint properties already covered by video interfaces schema
 4. Fix example indentation to 4 spaces for readability
- In patch 2								[Jani]
 1. Use connector->display_info from DRM helper instead of parsing EDID manually
- In patch 2								[Dmitry]
 1. Remove redundant powered check in reg access
 2. Use TMDS character rate instead of pixel clock for N/CTS
 3. Use consistent lowercase naming for tmds.
 4. Use test_bit() instead of custom bit-test helper
 5. Use tmds_char_rate_valid instead of custom mode_valid
 6. Use custom EDID read instead of DDC bus for segment handling
 7. Drop redundant atomic feature check
 8. Pass flags directly to drm_bridge_attach()
 9. Check DRM_BRIDGE_ATTACH_NO_CONNECTOR flag before drm_bridge_attach()
 10. Short-circuit HPD update if connector status unchanged
 11. Remove unnecessary NULL check for connector state
 12. Rename cached_edid to edid since it's no longer cached
 13. Remove redundant sample rate checks; rely on hdmi-codec validation
 14. Remove unsupported 18-bit audio sample size; rely on hdmi-codec
 15. Remove unnecessary fmt switch; rely on hdmi-codec defaults
 16. Check and propagate errors from it61620_audio_update_hw_params instead of
     ignoring them
- In patch 3								[Krzysztof]
 1. Remove unnecessary T: field pointing to git; subsystem already defines it
- Link to v3: https://lore.kernel.org/r/20251009-it61620-0714-v3-0-5d682d028441@ite.com.tw

Changes in v3:
- Wrapped description lines to comply with 80-character line length limit
  in patch 1.								[Rob]
- Renamed node from "it61620@58" to "bridge@58" in patch 1.		[Rob]
- Add port@2 for I2S audio input in patch 1.				[Dmitry]
- Updated the Kconfig dependency from CRYPTO and CRYPTO_HASH to 
  CRYPTO_LIB_SHA1 in patch 2.						[Eric]
- In patch 2								[Dmitry]
 1. Audio and InfoFrame
   - Rename audfmt to i2s_input_format for clarity.
   - Remove unused infoframe[HDMI_INFOFRAME_SIZE(AUDIO)].
 2. Platform data and structure
   - Drop platform data usage; migrate members into struct it61620
 3. Code organization
   - Reorder functions to avoid the need for forward declarations.
   - Add static inline to small helper functions
     (e.g. bridge_to_it61620()).
 4. HDCP handling
   - Make HDCP enable/disable conditional on conn_state->content_protection.
   - Report authentication result using drm_hdcp_update_content_protection().
 5. Error handling
   - Replace manual error path with dev_err_probe().
 6. Power management
   - Inline suspend/resume callbacks.
   - Use DEFINE_RUNTIME_DEV_PM_OPS() instead of explicit struct definition.
 7. Bridge callbacks
   - Drop empty bridge_detach().
   - Inline it61620_bridge_mode_valid().
 8. EDID handling
   - Remove unnecessary cached EDID duplication.
 9. Mode set and pixel clock
   - Move mode handling to atomic_enable().
   - Keep only pixelclock for future N/CTS audio calculations.
 10. Logging
    - Replace noisy drm_err() calls with drm_dbg().
 11. InfoFrame support
    - Add support for SPD and Vendor InfoFrames.
- Link to v2: https://lore.kernel.org/r/20250828-it61620-0714-v2-0-586f5934d5f8@ite.com.tw

Changes in v2:
- Call the sha1() library function instead of using the crypto_shash
  "sha1" in patch 2.
- Rewrite it61620_hdmi_ddc_wait() with readx_poll_timeout() in patch 2.	[Pin-yen]
- Rewrite it61620_hdmi_hdcp_wait_ksv_list() with readx_poll_timeout() in
  patch 2.
- Replace interrupts-extended with interrupts in patch 1.		[Rob]
- Replace dsi-lanes with the standard property data-lanes from the graph
  binding.								[Rob]
- Replace "#/$defs/port-base" with "#/properties/port" in patch 1.	[Rob]
- Drop unused labels and "hdmi" for the node name.			[Rob]
- Drop status in patch 1.						[Rob]
- Link to v1: https://lore.kernel.org/r/20250714-it61620-0714-v1-0-3761164d0b98@ite.com.tw

---
Pet Weng (2):
      dt-bindings: display: Add ITE IT61620 MIPI DSI to HDMI bridge
      drm/bridge: Add ITE IT61620 MIPI DSI to HDMI bridge driver

 .../bindings/display/bridge/ite,it61620.yaml       |  152 ++
 MAINTAINERS                                        |    7 +
 drivers/gpu/drm/bridge/Kconfig                     |   18 +
 drivers/gpu/drm/bridge/Makefile                    |    1 +
 drivers/gpu/drm/bridge/ite-it61620.c               | 2592 ++++++++++++++++++++
 5 files changed, 2770 insertions(+)
---
base-commit: a42c0d615ad29e3e11b1c91f677bcabcb5dc8e13
change-id: 20250714-it61620-0714-ab4ab4ceff29

Best regards,
-- 
Pet Weng <pet.weng@ite.com.tw>


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox